PDF tidak mengenal konsep font variabel. Font yang disematkan dalam berkas PDF adalah kumpulan outline tetap dengan metrik tetap, sehingga font variabel harus direduksi menjadi satu instance statis sebelum bisa masuk ke dalam dokumen. HotPDF melakukan instancing itu secara internal: Anda memeriksa axis pada font variabel, memilih koordinat seperti weight 620 atau width 87.5, dan library membakukan nilai-nilai tersebut ke dalam program font lengkap dan mandiri yang dapat dirender oleh pembaca PDF mana pun yang conforming
Alasan hal ini penting bersifat praktis, bukan teoretis. Foundry tipe semakin sering mengirimkan satu berkas variabel alih-alih selusin weight statis, dan tim desain memilih nilai yang tidak disediakan oleh named instance mana pun. Tanpa instancing, generator laporan akan jatuh kembali ke instance default, yang membuang keputusan desain tersebut, atau menyematkan seluruh font variabel dan berharap penampil menghormati koordinat axis yang tidak diketahuinya cara membacanya, padahal tidak ada pembaca yang diwajibkan melakukan itu
Apa sebenarnya yang harus dibangun ulang oleh instancing?
Font variabel OpenType menyimpan satu outline default per glyph ditambah sekumpulan delta yang diindeks berdasarkan posisi dalam design space. Menerapkan koordinat axis bukan sekadar menuliskan angka ke header; ini berarti menelusuri tabel gvar, menginterpolasi delta untuk lokasi yang diminta, memindahkan titik, lalu menghitung ulang segala sesuatu yang diturunkan dari titik-titik tersebut. HotPDF membangun ulang outline glyph, tabel loca versi panjang, metrik horizontal dan vertikal lengkap, bounding box font global, dan penyesuaian checksum sfnt
Sama pentingnya adalah apa yang dihapus. Instance statis tidak boleh menyimpan fvar, avar, gvar, HVAR, VVAR, MVAR, STAT, atau cvar, dan DSIG yang sudah usang pun harus dibuang, karena byte yang ditandatangani sudah tidak ada lagi. Membiarkan salah satu dari ini tertinggal menghasilkan font yang mengaku variabel padahal membawa outline yang sudah dipindahkan, dan pembaca yang memang menerapkan variasi akan menerapkannya untuk kedua kalinya
Phantom point, dan jebakan penerapan ganda
Aturan paling halus dalam seluruh proses ini menyangkut metrik. Dalam gvar, jumlah titik untuk sebuah glyph mencakup titik outline, atau titik komponen untuk glyph komposit, ditambah empat phantom point yang menyandikan left side bearing, advance width, dan padanan vertikalnya. Phantom point tersebut juga tunduk pada delta
Jadi ketika sebuah font memiliki tabel gvar, HotPDF menurunkan metrik horizontal dan vertikal dari phantom point yang telah diinterpolasi dan tidak menerapkan HVAR atau VVAR tambahan. Menerapkan keduanya adalah kesalahan klasik: variasi yang sama diterapkan dua kali dan setiap advance width menjadi sedikit terlalu lebar, yang terlihat sebagai teks yang perlahan-lahan bergeser ke kanan sepanjang baris rata kanan-kiri (justified). Hanya ketika sebuah font tidak memiliki gvar, library ini membakukan penyimpanan variasi metrik langsung ke hmtx atau vmtx
Dua detail lain menjaga geometri tetap akurat. Phantom point tidak pernah ikut serta dalam interpolasi contour, sehingga titik yang tidak secara eksplisit terdaftar untuk glyph sederhana disimpulkan dengan IUP per contour, dengan phantom point dikecualikan. Dan glyph komposit mendapatkan delta yang diterapkan pada offset komponen yang menggunakan parameter XY, setelah itu batas child dihitung ulang secara rekursif. Rekursi tersebut dibatasi kedalamannya dan diperiksa siklusnya, karena graf komponen yang jahat atau sekadar rusak jika tidak dapat berulang tanpa henti
Memeriksa design space sebelum memilih
Panggilan pertama dalam alur kerja instancing mana pun adalah InspectVariableFont, yang melaporkan axis dan named instance yang didefinisikan oleh foundry. Rekaman axis membawa tag empat byte, nilai minimum, default, dan maksimum, flag, serta name ID; named instance membawa subfamily name ID, flag, PostScript name ID opsional, dan satu koordinat per axis:
var
Pdf: THotPDF;
Axes: THPDFVariableFontAxisArray;
Instances: THPDFVariableFontNamedInstanceArray;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.InspectVariableFont('C:\Fonts\Inter.ttf', Axes, Instances) then
begin
for I := 0 to High(Axes) do
Writeln(Format('%s min=%.1f default=%.1f max=%.1f',
[string(Axes[I].Tag), Axes[I].MinimumValue,
Axes[I].DefaultValue, Axes[I].MaximumValue]));
Writeln(Format('%d named instance(s) defined', [Length(Instances)]));
end
else
Writeln('not a variable font - embed it as an ordinary TrueType face');
finally
Pdf.Free;
end;
end;
Melaporkan rentang axis itu penting karena nilai axis dibatasi (clamped) ke rentang yang dideklarasikan font, bukan ke rentang yang ditawarkan UI Anda. Slider yang memungkinkan pengguna meminta weight 1000 pada font yang axis wght-nya berhenti di 900 harus dikoreksi di antarmuka, bukan secara diam-diam di lapisan font, atau output cetak akan berbeda dari pratinjau
Memilih koordinat dan menerbitkan dokumen
Pemilihan axis bersifat stateful dan berlaku untuk font yang didaftarkan sesudahnya. SetVariableFontAxis menerima tag ASCII tercetak empat byte dan nilai finit, dan menolak apa pun selain itu dengan exception, bukan diam-diam mengabaikannya. ClearVariableFontAxes mengatur ulang pemilihan, dan GetVariableFontAxisSelections melaporkan apa yang sedang tertunda saat ini, yang layak dicatat dalam log pada mesin laporan di mana beberapa jalur kode mungkin telah menyentuh objek dokumen yang sama. Keluarga font itu sendiri dipilih berdasarkan nama melalui SetFont, persis seperti font TrueType yang disematkan lainnya:
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginDoc;
Pdf.SetVariableFontAxis('wght', 620); // semibold, bukan named instance
Pdf.SetVariableFontAxis('wdth', 87.5); // sedikit condensed
Pdf.CurrentPage.SetFont('Inter', [], 11);
Pdf.CurrentPage.TextOut(72, 720, 0, 'Quarterly results');
Pdf.ClearVariableFontAxes; // kembali ke instance default
Pdf.CurrentPage.SetFont('Inter', [], 10);
Pdf.CurrentPage.TextOut(72, 700, 0, 'Prepared by the finance team');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Event OnVariableFontInstance terpicu setiap kali sebuah instance dihasilkan dan melaporkan nilai axis yang digunakan, yang merupakan cara termurah untuk membuktikan dalam log apa yang sebenarnya terkandung dalam sebuah PDF. Karena setiap kumpulan koordinat yang berbeda menghasilkan program font yang berbeda, perlakukan pemilihan axis sebagai bagian dari kunci cache font Anda; mekanisme caching-nya dijelaskan dalam cache subset font persisten
Bagaimana instancing berinteraksi dengan subsetting dan shaping?
Instancing berjalan sebelum subsetting, dan urutan itu sudah benar. Font yang telah di-instance adalah font TrueType statis biasa, sehingga subsetter biasa memperlakukannya seperti font lainnya: ia menghitung glyph closure, menyimpan glyph yang benar-benar digunakan dokumen, dan membuang sisanya. Interaksi yang perlu diwaspadai adalah bahwa dua pemilihan axis berbeda dari keluarga yang sama merupakan dua program font yang berbeda, sehingga dokumen yang mencampur weight 400 dan weight 620 menyematkan dua subset, bukan satu face bersama dengan dua instance
Shaping pada dasarnya tidak terpengaruh dan layak diperiksa dalam praktik. Fitur layout berada di GSUB dan GPOS, yang dipertahankan oleh instancing, sehingga ligature dan alternate stilistik tetap berfungsi seperti dijelaskan dalam alternate stilistik GSUB OpenType. Yang berubah adalah positioning: instance condensed memiliki advance yang lebih sempit daripada default, sehingga layout mana pun yang mengukur teks sebelum instancing mengukur lebar yang salah. Ukurlah dengan pemilihan axis yang sama dengan yang akan Anda gunakan untuk merender, maka perbedaan itu akan hilang
Satu catatan defensif terakhir dari implementasi, berguna bagi siapa pun yang memperluas jalur ini. Font tanpa metrik vertikal tetap mengevaluasi argumen dynamic array di call site Delphi, sehingga array parse-time selalu dialokasikan alih-alih mengandalkan pemeriksaan HasVerticalMetrics untuk short-circuit melewati indeks kosong. Ini adalah jenis detail level bahasa yang mengubah cabang yang tampaknya sudah terjaga menjadi access violation tepat pada font yang tidak Anda uji
Dukungan font variabel masuk ke dalam pipeline font yang sama dengan penyematan, subsetting, dan glyph closure, yang dijelaskan lebih mendalam dalam glyph closure subset font dan glyph yang dibentuk. Daftar fitur tipografi lengkap untuk Delphi dan C++Builder tercantum di halaman komponen PDF Delphi HotPDF