Ketika sebuah PDF tak menanam font, komponen HotPDF merender teks itu dengan font Windows terinstal yang dipilih HPDFMapBaseFontToSystem: ia men-decode nama /BaseFont, mencabut bagian style, mencoba beberapa ejaan sampai GDI memastikan family-nya terinstal, mengukur lebar standard 14 yang hilang dari font yang kompatibel metrik, dan mengonversi kode single-byte ke Unicode sebelum menggambar. Setiap langkah itu ada karena versi naifnya gagal di file nyata. Page renderer RenderLoadedPageToBitmap menangani font program tertanam dengan baik; ini kisah font yang sama sekali tak ada di file
Kenapa GDI diam-diam menggambar typeface yang salah untuk font tak tertanam?
GDI tak pernah melaporkan font yang hilang: serahkan CreateFontIndirect sebuah nama face yang tak dikenalnya dan ia diam-diam memilih pengganti, sering kali serif berbeda tanpa weight bold. Renderer awal meneruskan nama PDF hampir apa adanya, jadi TimesNewRoman,Bold, TimesNewRomanPS-BoldMT, dan SegoeUI-Semibold semuanya tak cocok dengan apa pun dan keluar dalam apa pun pilihan GDI. Nama bisa lebih buruk dari itu. ISO 32000-1 §7.3.5 membolehkan sebuah name menulis byte mana pun sebagai #xx, dan produser CJK lazim mengeja nama font sebagai UTF-8 ter-escape atau byte code page legacy; sebelum v2.766.69 escape-nya sendiri menjadi nama face. HPDFMapBaseFontToSystem kini men-decode escape lebih dulu, mengembalikan urutan byte UTF-8 yang valid sebagai karakternya, dan membaca byte tinggi lainnya dalam code page sistem
Mencabut style adalah tempat heuristik menggigit. Koma selalu mengakhiri family (Arial,Bold memberi Arial), tapi tanda hubung hanya melakukannya ketika kata setelahnya adalah style: Bold, Italic, Oblique, Regular, Roman, Medium, Light, Black, Heavy, Semi, Demi, Thin, Extra, Ultra, atau Condensed. Aturan itu menjaga MS-Mincho tetap utuh sambil mengubah Calibri-Light menjadi Calibri. HotPDF lalu mencoba ejaan-ejaan family, disusul ejaan family dengan sufiks PSMT, MT, atau PS yang dicabut. Sejak v2.768.18 ejaan-ejaan itu mencakup setiap pilihan spasi di tempat-tempat sebuah kata bisa dimulai: sebelum kapital yang mengikuti huruf kecil (MyriadPro menjadi Myriad Pro), pada kapital terakhir dari run yang diikuti huruf kecil (UIGothic), dan setelah MS di depan (MSPGothic), dari bentuk penuh spasi sampai nama apa adanya; lebih dari empat tempat semacam itu, hanya bentuk berpenuh spasi dan nama tersambung yang dicoba. Spasi tak bisa diterapkan membabi buta, karena Windows menyimpan beberapa kata dalam bentuk tersambung: SimSun terinstal dengan ejaan persis itu, sedangkan MicrosoftYaHei, MicrosoftJhengHei, dan MSPGothic adalah milik Microsoft YaHei, Microsoft JhengHei, dan MS PGothic. Sebelum v2.768.18 mapper menyelipkan spasi sebelum setiap kapital di dalam, sehingga MicrosoftYaHei dicari sebagai Microsoft Ya Hei dan tak pernah ketemu. Seorang kandidat dianggap terinstal ketika CreateFontIndirect disusul GetTextFace mengembalikan nama yang diminta atau, sejak v2.768.18, ketika tabel name font terpilih mencantumkannya sebagai family, full atau typographic family name dalam bahasa apa pun; jawabannya di-cache per nama, jadi dokumen dengan banyak font yang tak terinstal tak lagi memrobe Windows untuk setiap nama di setiap halaman
Karena fungsi mappingnya publik di unit HPDFRenderFontMetrics, laporan preflight bisa menampilkan family terinstal mana yang akan dipakai merender setiap font tak tertanam, dengan enumerasi font yang THotPDF sudah ekspos untuk dokumen termuat:
uses
HPDFDoc, HPDFRenderFontMetrics;
procedure ListSystemFontMappings(Pdf: THotPDF; Log: TStrings);
var
Page, I: Integer;
Info: THPDFLoadedFontInfo;
begin
for Page := 0 to Pdf.LoadedPageCount - 1 do
for I := 0 to Pdf.GetLoadedFontCount(Page) - 1 do
if Pdf.GetLoadedFontInfo(Page, I, Info) and not Info.IsEmbedded then
Log.Add(Format('page %d /%s %s -> %s',
[Page + 1, string(Info.ResourceName), string(Info.FontName),
HPDFMapBaseFontToSystem(Info.FontName)]));
end;
Bagaimana HotPDF mengukur font standard 14 yang tak punya /Widths?
HotPDF mengukur advance yang hilang pada font terinstal dengan metrik yang sama, karena ISO 32000-1 §9.6.2.2 membolehkan font standard 14 menghilangkan /Widths dan library ini tak mengirim tabel AFM. Arial membawa metrik Helvetica, Times New Roman membawa Times, dan Courier New membawa Courier, jadi HPDFMeasureBaseFontWidths membuat face yang cocok pada lfHeight = -1000 dan memanggil GetCharWidth32W; pada tinggi itu hasilnya sudah dalam satuan 1/1000 em yang dipakai lebar PDF. Renderer lebih dulu mengubah setiap kode menjadi Unicode lewat /Encoding, /BaseEncoding, dan /Differences, dengan default StandardEncoding. Export SVG dan ekstraksi teks menginjak satu jebakan lagi: font Type 1 standar tanpa /Encoding sama sekali menghasilkan decoder tanpa informasi encoding, export SVG tak pernah mendaftarkannya, dan semua lebar hasil pengukuran tak terpakai. Menyuplai StandardEncoding yang tersirat memperbaikinya, asalkan ditandai sebagai encoding predefined; mengarahkannya ke jalur nama CMap mendekode setiap kode sebagai 0 dan semua lebar ikut salah
Bold, italic, dan satu-salah-urut di flag font descriptor
Entri /Flags sebuah font descriptor menomori bit-bitnya dari 1, bukan 0, jadi ForceBold adalah bit 19 ($40000) dan Italic bit 7 ($40), menurut ISO 32000-1 Tabel 123. Kode lama menguji $20000, yang merupakan bit 18, SmallCap. Kesalahan itu selamat dari v2.345.0 sampai v2.766.53 karena /FontDescriptor hampir selalu berupa referensi tak langsung dan font builder hanya membaca objek langsung, sehingga seluruh cabang flags tak pernah berjalan, dan kebutaan yang sama mengabaikan /Widths 12 0 R serta menata teks dengan advance fallback 500 unit. Begitu v2.766.53 mulai meresolusi referensi tak langsung lewat renderer, bit itu harus dikoreksi dalam perubahan yang sama, kalau tidak setiap face small-caps mendadak terrender bold:
const
// ISO 32000-1 Tabel 123 menghitung posisi bit dari 1
FD_ITALIC = $00040; // bit 7
FD_SMALLCAP = $20000; // bit 18, bukan weight
FD_FORCEBOLD = $40000; // bit 19
procedure ApplyDescriptorFlags(Flags: Integer; var LF: TLogFont);
begin
if (Flags and FD_FORCEBOLD) <> 0 then
LF.lfWeight := FW_BOLD;
if (Flags and FD_ITALIC) <> 0 then
LF.lfItalic := 1;
end;
Kenapa font CJK dengan CMap UCS2 mendapat lebar yang salah?
Teks CJK dengan CMap UCS2 predefined menggambar glyph yang tepat tapi spacing yang salah ketika renderer memperlakukan kode sebagai CID, karena /W diindeks oleh CID, bukan oleh kode karakter. Dengan STSong-Light dan UniGB-UCS2-H, kebetulan kodenya sama dengan nilai Unicode, jadi GDI menggambar karakter yang benar dan bug-nya bersembunyi di advance: huruf kecil datang sebagai kode 97 ke atas, jatuh di luar entri /W seperti [1 95 500], dan semuanya menerima lebar default /DW sebesar 1000. Sejak v2.766.56 renderer HotPDF membaca kode lewat rentang codespace CMap (ISO 32000-1 §9.7.6.2) dan memetakannya ke CID sebelum mencari lebar. Hanya tabel UCS2 dan UTF16 bawaan serta stream CMap tertanam yang dipakai; aproksimasi identitas untuk yang seperti GBK-EUC-H hanya akan tampak didukung sambil menghasilkan output yang salah, jadi renderer ini tak berpura-pura
Kenapa karakter beraksen berubah jadi tanda tanya di Windows China?
Kode single-byte tak boleh pernah sampai ke fungsi GDI ANSI ("A"), karena GetGlyphOutlineA dan GetGlyphIndicesA menafsirkan byte dalam code page sistem sementara TextOutA memakai character set font terpilih. Di sistem China, byte $A9 milik Arial (tanda hak cipta di Windows-1252) menjadi lead byte GBK dan terrender sebagai "?", jebakan yang langsung diinjak jalur outline tanpa hint yang ditambahkan di v2.766.83. v2.767.3 menanyakan character set milik font terealisasi dengan GetTextCharset, mengonversinya ke code page lewat TranslateCharsetInfo, menjalankan byte lewat MultiByteToWideChar, dan memanggil fungsi W; font simbol memakai U+F000 plus kodenya sebagai gantinya. Encoding yang tak sepaham dengan Windows-1252 — /Differences, StandardEncoding, MacRomanEncoding — dipetakan ke Unicode sebelum font sistem mana pun melihatnya
Apa batas menggambar dengan font sistem?
Rendering font sistem adalah aproksimasi, dan komponen HotPDF jujur soal di mana ia berhenti. Sebelum v2.768.18 pengecekan instalasi hanya membandingkan nama yang dikembalikan GetTextFace, dan di Windows terlokalisasi fungsi itu melaporkan nama family dalam bahasa sistem, sehingga Microsoft YaHei di Windows China atau Yu Mincho di Windows Jepang dinilai hilang dan digambar dengan pengganti GDI; sejak v2.768.18 face yang kembali dengan nama lain juga dicari di tabel name font, dan font semacam itu ditemukan. Kompatibilitas metrik hanya dijamin untuk keluarga Helvetica, Times, dan Courier; Symbol dipetakan ke Symbol dan ZapfDingbats ke Wingdings, yang merupakan solusi darurat alih-alih kecocokan. Preflight di atas juga hanya melihat font di dictionary /Resources setiap halaman, bukan yang direferensikan dari dalam form XObject. Ketika sebuah kode tetap tak bisa digambar, pelacakan glyph tak teresolusi saat menggambar melaporkannya, sinyal yang lebih baik daripada mengamati thumbnail sepintas
Perbaikan yang tahan lama duduk di sisi penulisan. HotPDF sendiri menulis dengan FontEmbedding disetel True secara default, menggantikan Arial tertanam bahkan ketika kode memanggil SetFont dengan Helvetica, dan teks tertanam melewati renderer glyph font tertanam alih-alih tebak-tebakan di atas. Penjaga murah untuk file yang masuk adalah memperingatkan sebelum merender ketika family hasil mapping tak ada di daftar font screen:
// VCL: Screen.Fonts mencantumkan nama family terinstal (unit Forms)
function MissingSystemFamily(const BaseFont: AnsiString): Boolean;
begin
Result := Screen.Fonts.IndexOf(HPDFMapBaseFontToSystem(BaseFont)) < 0;
end;
Untuk komponen lengkap, termasuk rendering halaman, ekstraksi teks, dan font subsetting di sisi penulisan, lihat halaman produk HotPDF Delphi PDF component