TrueType Font in pdfTeX - V: Virtual Fonts, Ligatures, and OpenType

This blog is totally generated by LLM (I just check part of it)

In the previous post, TrueType Font in pdfTeX - IV, we loaded IBM Plex TrueType fonts in pdfTeX by creating a TeX font metric (.tfm), a LaTeX font definition (.fd), and a font map (.map). That was enough to typeset basic text, but several questions remained:

  1. How do we preserve ligatures, kerning, and the usual TeX conversions such as -- and ---?
  2. What should we do about missing characters reported by ttf2tfm?
  3. How can the same TTF files be used with XeLaTeX and LuaLaTeX?
  4. Is autoinst / otftotfm a better alternative to ttf2tfm?

This post finishes those experiments. The focus remains on understanding the traditional TeX font machinery rather than simply installing a ready-made font package.

Scope. We are working with ordinary, static TrueType font files and the T1 text encoding. This is not a tutorial about variable-font axes, math fonts, or complex-script shaping. The IBM Plex file names used below are examples; check the exact names in your own download.

1. The font pipeline: TFM, VF, and map files

A useful distinction is between the font seen by TeX and the font ultimately embedded in the PDF.

1
2
3
4
5
6
7
8
9
10
11
12
13
    TeX / NFSS
|
virtual TFM
|
virtual font (.vf)
|
raw TFM
|
pdfTeX font map
|
TrueType file (.ttf)
|
PDF
  • TFM provides dimensions and the traditional TeX ligature/kerning program. TeX uses font metrics while breaking paragraphs; it does not need the glyph outlines at this stage.
  • VF (virtual font) defines how a character in one TeX font is assembled from characters or rules in one or more physical fonts. A virtual glyph need not correspond to a single glyph in the source TTF.
  • ENC supplies the mapping between the 8-bit character slots used by a physical TeX font and names of glyphs in the outline font.
  • MAP tells the PDF backend how to find and embed the physical outline font. A virtual font is not itself an outline font to embed.
  • FD tells LaTeX’s New Font Selection Scheme (NFSS) which TFM corresponds to a given encoding, family, series, and shape.

The distinction matters because ttf2tfm -v creates a raw TFM and a VPL (Virtual Property List). vptovf then converts the VPL into a virtual TFM and VF.

1.1 Why -T is not the whole story

Our earlier command was:

1
2
3
ttf2tfm IBMPlexSerif-Regular.ttf \
-T T1-WGL4.enc \
T1-IBMPlexSerif-Regular.tfm

In ttf2tfm, -T foo.enc is shorthand for specifying the same encoding as both the input/raw (-p) and output/virtual (-t) encoding. It does not, on its own, request a virtual font.

To generate the intermediate VPL, use -v:

1
2
3
4
5
6
7
8
ttf2tfm IBMPlexSerif-Regular.ttf \
-T T1-WGL4.enc \
-v T1-IBMPlexSerif-Regular.vpl \
T1-IBMPlexSerif-Regular-raw.tfm

vptovf T1-IBMPlexSerif-Regular.vpl \
T1-IBMPlexSerif-Regular.vf \
T1-IBMPlexSerif-Regular.tfm

There are now two different metrics:

1
2
3
4
T1-IBMPlexSerif-Regular-raw.tfm   # physical/raw font
T1-IBMPlexSerif-Regular.tfm # virtual font selected by LaTeX
T1-IBMPlexSerif-Regular.vf # virtual character definitions
T1-IBMPlexSerif-Regular.vpl # readable source for the VF/TFM

Do not reuse the same font name for the raw and virtual TFM. The ttf2tfm manual explicitly warns about this.

The map entry must refer to the raw TFM:

1
T1-IBMPlexSerif-Regular-raw < T1-WGL4.enc < IBMPlexSerif-Regular.ttf

Meanwhile, the LaTeX .fd file selects T1-IBMPlexSerif-Regular (without -raw). The VF bridges the two.

2. Ligatures, kerning, and dashes

2.1 TeX ligatures are programs in the metrics

Traditional TeX handles ligatures and pair kerning while building the paragraph. Its TFM contains a small ligature/kerning program, which can be inspected in the human-readable PL/VPL representation.

For example, an illustrative PL fragment might contain:

1
2
3
4
5
6
(LIGTABLE
(LABEL C f)
(LIG C i O 34)
(LIG C l O 35)
(STOP)
)

Here O 34 and O 35 are octal character slots for fi and fl in T1. The ligature is represented by a character slot in the TeX font; it does not require TeX itself to understand OpenType GSUB tables.

A kerning rule can look like:

1
2
3
4
5
(LIGTABLE
(LABEL C A)
(KRN C V R -80)
(STOP)
)

The value is illustrative, not an IBM Plex measurement. In a VPL with (DESIGNUNITS R 1000), the dimensions are given in thousandths of the design size; do not copy this value into a real font without checking it.

Inspect the generated files with:

1
2
3
4
5
6
7
8
# The readable input used to generate the VF:
grep -n -A25 '(LIGTABLE' T1-IBMPlexSerif-Regular.vpl

# Or convert the final virtual TFM back to a PL file:
tftopl T1-IBMPlexSerif-Regular.tfm \
T1-IBMPlexSerif-Regular.pl

grep -n 'LIG\|KRN' T1-IBMPlexSerif-Regular.pl

Do not assume that a command succeeded merely because the PDF compiles. An empty or incomplete LIGTABLE may mean the required font data was never imported.

2.2 The T1-WGL4.enc file contains more than a glyph list

One particularly useful feature of T1-WGL4.enc is its special % LIGKERN comments, which are understood by ttf2tfm:

1
2
3
4
5
6
7
8
9
% LIGKERN hyphen hyphen =: endash ;
% LIGKERN endash hyphen =: emdash ;
% LIGKERN quoteleft quoteleft =: quotedblleft ;
% LIGKERN quoteright quoteright =: quotedblright ;
% LIGKERN f i =: fi ;
% LIGKERN f l =: fl ;
% LIGKERN f f =: ff ;
% LIGKERN ff i =: ffi ;
% LIGKERN ff l =: ffl ;

For example, the two-step replacement

1
2
--   -> endash
--- -> emdash

can be described by ligature rules in the font metrics. Similarly, `` and '' can become typographic quotation marks.

But there is an important condition: the target character must have a usable definition in the generated font. A rule referring to a missing ff character does not magically create an ff glyph. If ff, ffi, or ffl cannot be mapped, check which rules actually survived in the resulting VPL.

2.3 Why OpenType kerning may still be missing

TrueType/OpenType fonts may store kerning in either a legacy kern table or the newer GPOS layout table. Ligature substitution can likewise be defined through GSUB.

ttf2tfm can extract traditional TrueType metric/kerning information and process its own encoding-based ligature rules. It is not a full OpenType shaping engine; do not assume it will extract every GSUB/GPOS rule.

A practical consequence: a font may show kerning in XeLaTeX/LuaLaTeX but little or no kerning in a TFM created by ttf2tfm.

3. Missing characters: first diagnose, then fix

The warnings in Part IV included names such as:

1
2
3
4
5
6
7
8
macron
compwordmark
perthousandzero
dotlessj
ff
ffi
ffl
visualspace

Not every warning has the same cause.

Situation Likely remedy
Glyph exists, but has a different name Change the encoding or rename glyph mappings with ttf2tfm -r / -R
Glyph exists but is reachable only via GSUB Use an OpenType-aware converter or an appropriate explicit mapping
Glyph is genuinely absent Compose a virtual character, use a fallback, or edit the font
TeX-specific pseudo-character has no ordinary glyph Implement the appropriate TeX-level behavior or virtual-font construction
hyphen is mapped twice Recognize the encoding duplication warning; do not treat it as proof of a broken TTF

3.1 Check Unicode mappings and OpenType tables

The Python package fontTools is useful here:

1
python -m pip install fonttools
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from fontTools.ttLib import TTFont

font = TTFont("IBM_Plex_Serif/IBMPlexSerif-Regular.ttf")
cmap = font.getBestCmap() or {}

for name, codepoint in {
"macron": 0x00AF,
"dotlessj": 0x0237,
"ff": 0xFB00,
"fi": 0xFB01,
"fl": 0xFB02,
"ffi": 0xFB03,
"ffl": 0xFB04,
"endash": 0x2013,
"emdash": 0x2014,
}.items():
print(f"{name:10} U+{codepoint:04X}: {cmap.get(codepoint)}")

print("GSUB:", "GSUB" in font)
print("GPOS:", "GPOS" in font)
print("kern:", "kern" in font)

The cmap only answers whether a Unicode code point is mapped to a glyph. A ligature glyph might exist without a Unicode mapping, accessible only through GSUB. Therefore, absence from getBestCmap() is not conclusive evidence that the glyph is absent from the font.

For a more direct OpenType feature inventory, use:

1
otfinfo -f IBM_Plex_Serif/IBMPlexSerif-Regular.ttf

3.2 Compose a missing character in a virtual font

A VF can assemble one logical TeX character from several physical glyphs. Suppose we want a fallback ff character, but the physical TTF provides only f.

A schematic VPL definition (T1 slot O 33 is ff) is:

1
2
3
4
5
6
7
8
9
(CHARACTER O 33 (comment ff)
(CHARWD R 666)
(CHARHT R 700)
(MAP
(SELECTFONT D 0)
(SETCHAR C f)
(SETCHAR C f)
)
)

This is not a drop-in patch: the character dimensions are placeholders; the selected raw font must have a usable f at that slot; any advance adjustments or kerning must be calculated; and the corresponding LIGTABLE entry must refer to the constructed ff character. Preserve the generated VPL’s other records, especially MAPFONT and existing ligature rules.

After editing the VPL, regenerate the VF and virtual TFM:

1
2
3
vptovf T1-IBMPlexSerif-Regular.vpl \
T1-IBMPlexSerif-Regular.vf \
T1-IBMPlexSerif-Regular.tfm

This fallback makes the two f glyphs act as one TeX character, but it does not recreate a type designer’s original ff ligature outline. It can also have limitations for copy/paste and Unicode extraction from the PDF. Prefer mapping to an authentic glyph when possible.

4. Batch conversion for my actual directory layout

I now keep the fonts in two subdirectories:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── font_trans_batch.sh
├── IBM_Plex_Mono/
│ ├── IBMPlexMono-Regular.ttf
│ ├── IBMPlexMono-Italic.ttf
│ ├── IBMPlexMono-Bold.ttf
│ └── IBMPlexMono-BoldItalic.ttf
├── IBM_Plex_Serif/
│ ├── IBMPlexSerif-Regular.ttf
│ ├── IBMPlexSerif-Italic.ttf
│ ├── IBMPlexSerif-Bold.ttf
│ └── IBMPlexSerif-BoldItalic.ttf
└── T1-WGL4.enc

The following Bash script processes every matching TTF in each directory. It writes the generated metrics and virtual fonts into the project root, along with one shared ibmplex.map.

font_trans_batch.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env bash
set -euo pipefail

# Make paths independent of the shell's current working directory.
cd -- "$(dirname -- "${BASH_SOURCE[0]}")"

enc="T1-WGL4.enc"
map="ibmplex.map"

for cmd in ttf2tfm vptovf; do
command -v "$cmd" >/dev/null 2>&1 || {
printf 'Missing required command: %s\n' "$cmd" >&2
exit 1
}
done

[[ -f "$enc" ]] || { echo "Missing $enc" >&2; exit 1; }

: > "$map"
count=0

for family in Serif Mono; do
dir="IBM_Plex_${family}"
[[ -d "$dir" ]] || { echo "Missing $dir" >&2; exit 1; }

for font in "$dir"/IBMPlex"$family"-*.ttf; do
[[ -f "$font" ]] || continue

base=${font##*/}
base=${base%.ttf}
name="T1-${base}"

printf 'Processing %s\n' "$font"

ttf2tfm "$font" -q -T "$enc" \
-v "${name}.vpl" \
"${name}-raw.tfm"

vptovf "${name}.vpl" \
"${name}.vf" \
"${name}.tfm"

# The map entry belongs to the *physical* (raw) font.
printf '%s-raw < %s < %s\n' \
"$name" "$enc" "$font" >> "$map"

((count += 1))
done
done

(( count > 0 )) || { echo "No matching TTF files found" >&2; exit 1; }
printf 'Generated files for %d fonts; see %s\n' "$count" "$map"

Run:

1
2
chmod +x font_trans_batch.sh
./font_trans_batch.sh

A typical map entry now looks like:

1
2
T1-IBMPlexSerif-Regular-raw < T1-WGL4.enc < IBM_Plex_Serif/IBMPlexSerif-Regular.ttf
T1-IBMPlexMono-Regular-raw < T1-WGL4.enc < IBM_Plex_Mono/IBMPlexMono-Regular.ttf

The subdirectory path is important. For a local experiment, compile the test document from the project root so that the map, metrics, VFs, and referenced TTF paths can all be found. For a system-wide installation, use a proper TEXMF directory structure instead.

Caution: This script discovers additional weights such as Light and Medium, but the .fd definitions below deliberately select only four styles. Converting a font and registering it with NFSS are separate operations. Also, regenerating VPLs overwrites manual edits, so save customized VPLs separately.

5. Two LaTeX font families: Serif and Mono

NFSS expects a font definition file named after the lower-case encoding and family. I will use:

1
2
t1ibmplexserif.fd
t1ibmplexmono.fd

Rather than treating sans-serif or monospace as a non-standard font shape, make each family independent. In standard NFSS terminology, shape n means upright and it means italic; the mono/serif distinction belongs to the family.

5.1 t1ibmplexserif.fd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
\ProvidesFile{t1ibmplexserif.fd}[2026/11/14 IBM Plex Serif T1]

\DeclareFontFamily{T1}{ibmplexserif}{}

\DeclareFontShape{T1}{ibmplexserif}{m}{n}
{<-> T1-IBMPlexSerif-Regular}{}
\DeclareFontShape{T1}{ibmplexserif}{m}{it}
{<-> T1-IBMPlexSerif-Italic}{}
\DeclareFontShape{T1}{ibmplexserif}{b}{n}
{<-> T1-IBMPlexSerif-Bold}{}
\DeclareFontShape{T1}{ibmplexserif}{b}{it}
{<-> T1-IBMPlexSerif-BoldItalic}{}

% Standard LaTeX \bfseries may request bx rather than b.
\DeclareFontShape{T1}{ibmplexserif}{bx}{n}
{<->ssub * ibmplexserif/b/n}{}
\DeclareFontShape{T1}{ibmplexserif}{bx}{it}
{<->ssub * ibmplexserif/b/it}{}

\endinput

5.2 t1ibmplexmono.fd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
\ProvidesFile{t1ibmplexmono.fd}[2026/11/14 IBM Plex Mono T1]

\DeclareFontFamily{T1}{ibmplexmono}{}

\DeclareFontShape{T1}{ibmplexmono}{m}{n}
{<-> T1-IBMPlexMono-Regular}{}
\DeclareFontShape{T1}{ibmplexmono}{m}{it}
{<-> T1-IBMPlexMono-Italic}{}
\DeclareFontShape{T1}{ibmplexmono}{b}{n}
{<-> T1-IBMPlexMono-Bold}{}
\DeclareFontShape{T1}{ibmplexmono}{b}{it}
{<-> T1-IBMPlexMono-BoldItalic}{}

\DeclareFontShape{T1}{ibmplexmono}{bx}{n}
{<->ssub * ibmplexmono/b/n}{}
\DeclareFontShape{T1}{ibmplexmono}{bx}{it}
{<->ssub * ibmplexmono/b/it}{}

\endinput

The bx substitution declarations make \textbf work even when our physical font series is registered as b. Also notice that I removed the old \hyphenchar\font=-1 family declaration; that setting would disable automatic hyphenation, which is normally undesirable for serif body text.

5.3 A pdfLaTeX test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
% !TeX program = pdfLaTeX
\documentclass{article}
\usepackage[T1]{fontenc}

\pdfmapfile{+ibmplex.map}
\renewcommand{\rmdefault}{ibmplexserif}
\renewcommand{\ttdefault}{ibmplexmono}

\begin{document}
\section{IBM Plex Serif}
AVATAR, To, Wa, Yo.\par
ff, fi, fl, ffi, ffl.\par
abc--def, abc---def.\par
``Hello, world!''\par
\textbf{Bold serif.}\par
\textit{Italic serif.}\par
\textbf{\textit{Bold italic serif.}}

\section{IBM Plex Mono}
\texttt{Regular mono.}\par
\texttt{\textbf{Bold mono.}}\par
\texttt{\textit{Italic mono.}}\par
\texttt{\textbf{\textit{Bold italic mono.}}}
\end{document}

Compile and inspect the result:

1
2
3
4
pdflatex -interaction=nonstopmode -halt-on-error test.tex
pdffonts test.pdf

grep -Ei 'warning|glyph .*not found|font shape|font .*not found' test.log

A successful compilation does not guarantee that all glyphs appeared correctly. In particular, check PDF font warnings and visually compare fi, fl, the other f-ligatures, and the dashes.

6. XeLaTeX and LuaLaTeX: no TFM/VF conversion is needed

For these engines, the ordinary route is fontspec, which can load the TTF directly and use the modern font-loading/shaping infrastructure.

In our directory layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
% !TeX program = LuaLaTeX
% The same file may be compiled with XeLaTeX.
\documentclass{article}
\usepackage{fontspec}

\setmainfont{IBMPlexSerif}[
Path = IBM_Plex_Serif/,
Extension = .ttf,
UprightFont = *-Regular,
BoldFont = *-Bold,
ItalicFont = *-Italic,
BoldItalicFont = *-BoldItalic,
Ligatures = {Common,TeX},
Kerning = On,
]

\setmonofont{IBMPlexMono}[
Path = IBM_Plex_Mono/,
Extension = .ttf,
UprightFont = *-Regular,
BoldFont = *-Bold,
ItalicFont = *-Italic,
BoldItalicFont = *-BoldItalic,
]

\begin{document}
AVATAR, To, Wa, Yo.\par
ff, fi, fl, ffi, ffl.\par
abc--def, abc---def.\par
\textbf{Bold.} \textit{Italic.} \textbf{\textit{Bold italic.}}\par
\texttt{IBM Plex Mono: a--b, fi, fl.}
\end{document}

The most relevant features are:

fontspec setting Purpose
Ligatures=Common Enable ordinary OpenType liga substitutions where available
Ligatures=TeX Request the traditional TeX-style quote/dash input mappings
Kerning=On Enable available font kerning

Do not confuse Ligatures=TeX with the OpenType liga feature: the former provides TeX-like input substitutions, while the latter selects typographic glyph substitutions. The exact outcome still depends on the font and renderer. For source-code text, you may prefer not to enable TeX punctuation substitutions in the mono font.

You do not need \pdfmapfile, ttf2tfm, or custom .fd files in this example. LuaLaTeX also does not need the old compatibility workaround \protected\def\pdfmapline{\pdfextension mapline } merely to load a Unicode TrueType font.

Traditional TeX fonts can still be used with XeTeX/LuaTeX, but that is a different, legacy route and not the reason to choose fontspec.

7. ttf2tfm versus otftotfm versus autoinst

The official plex package is a useful comparison. Its documentation says that its Type 1 fonts were made with cfftot1, and its LaTeX support files with autoinst.

The distinction is important: autoinst is not itself a TrueType-to-Type-1 converter. It is an automation layer around the LCDF TypeTools, most importantly otftotfm.

1
2
3
4
5
6
7
8
9
10
11
Our experimental route:
TTF -> ttf2tfm -> raw TFM + VPL -> vptovf -> TFM + VF
\-> handwritten .map and .fd

The OpenType-aware route:
OTF/TTF -> autoinst -> otftotfm -> TFM/VF/ENC/MAP
\-> generated NFSS .fd / .sty support

The CTAN plex package's Type 1 route:
CFF-based OTF -> cfftot1 -> PFB
\-> autoinst / otftotfm -> LaTeX support files

7.1 Capabilities

Property ttf2tfm otftotfm / autoinst
Original purpose Convert TrueType metrics to TeX Convert OpenType fonts and build LaTeX support
TrueType outlines Yes Supported by otftotfm, but some versions warn that support is experimental
CFF-based OpenType input Not its main input format Yes
TFM and virtual fonts Yes, via VPL and vptovf Yes
Traditional TeX ligature rules Via encoding directives and TFM Yes
GSUB/GPOS feature extraction Limited More capable, depending on supported feature/lookup
Generate .fd and .sty Handwritten Automated by autoinst
Best for Learning and manual control Repeatable font-package generation

The table is deliberately not a claim that otftotfm performs all modern shaping. The 8-bit TFM/VF model still has intrinsic limitations, and some OpenType features cannot be faithfully represented by a static conversion.

7.2 Inspect OpenType features

First check which features are actually present:

1
otfinfo -f IBM_Plex_Serif/IBMPlexSerif-Regular.ttf

Useful tags include liga (standard ligatures), kern (kerning), smcp (small caps), onum (oldstyle figures), and tnum (tabular figures). Feature availability is font- and style-dependent. Do not request a feature and assume it exists just because the command accepts the option.

An otftotfm experiment with a TTF might look like:

1
2
3
otftotfm -e fontools_t1 -fliga -fkern \
IBM_Plex_Serif/IBMPlexSerif-Regular.ttf \
PlexSerifExperiment

The fontools_t1 encoding is shipped with the fontools package. Unlike ttf2tfm, otftotfm requires an explicit encoding for TrueType-flavored input, and some installed releases report: TrueType-flavored font support is experimental. The exact features available depend on the version of LCDF TypeTools and the font.

7.3 Try autoinst without modifying the TeX installation

For a controlled experiment, use its manual mode:

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p autoinst_test/serif autoinst_test/mono

(
cd autoinst_test/serif
autoinst -manual -encoding=T1 -serif \
../../IBM_Plex_Serif/*.ttf
)

(
cd autoinst_test/mono
autoinst -manual -encoding=T1 -typewriter \
../../IBM_Plex_Mono/*.ttf
)

Manual mode is intentionally multi-step. First, autoinst analyzes font metadata and writes an autoinst.bat file containing the proposed otftotfm commands. It does not immediately execute all of those commands. Inspect the log and commands before proceeding:

1
2
less autoinst_test/serif/autoinst.bat
less autoinst_test/mono/autoinst.bat

After reviewing the commands, run each autoinst.bat from its own directory, and follow the manual-mode instructions to turn generated PL/VPL files into TFM/VF and register the map if you want a permanent installation. This keeps the experiment separate from the system TEXMF tree.

One benefit of autoinst is that it automatically discovers the available weights, italic shapes, and some optional OpenType features, then generates NFSS definitions rather than requiring us to maintain many nearly identical .fd files by hand.

8. Suggested verification checklist

  1. Confirm that the input TTF, encoding file, raw TFM, virtual TFM, and VF are all discoverable by TeX.
  2. Check that map entries refer to the raw font name and the correct TTF path.
  3. Inspect LIGTABLE and KRN entries rather than relying on visual appearance alone.
  4. Test fi, fl, ff, ffi, ffl, --, ---, and quotation marks.
  5. Compare the same test in pdfLaTeX, XeLaTeX, and LuaLaTeX.
  6. Read both the LaTeX log and the PDF embedding information from pdffonts.
  7. Compare the results with the official plex package or an autoinst-generated font package.

Conclusion

The three remaining tasks from Part IV lead to a useful distinction:

  • ttf2tfm + vptovf is a good way to learn how TeX’s metrics, ligature tables, and virtual fonts work. It gives us direct control but also makes missing glyphs and incomplete OpenType feature extraction our responsibility.
  • otftotfm + autoinst is a higher-level way to generate traditional TeX font support from OpenType fonts, with better access to available GSUB/GPOS information and much less handwritten NFSS boilerplate.
  • fontspec is the natural choice when compiling with XeLaTeX or LuaLaTeX: no 8-bit conversion pipeline is needed for these TTF files.

For this blog series, I will keep the ttf2tfm route as an educational experiment, while treating the generated files from autoinst as a useful reference implementation. The next interesting question is precisely how OpenType GSUB/GPOS information is approximated by TeX’s LIGTABLE, TFM metrics, and virtual-character mappings.

References

  1. ttf2tfm manual — raw and virtual encodings, -T, -v, glyph mapping and ligature rules.
  2. vptovf manual — converting VPL to VF and TFM.
  3. LCDF TypeTools (otftotfm) — TeX metrics from OpenType fonts.
  4. autoinst manual / fontools — automated generation of font support files and manual mode.
  5. fontspec documentation — OpenType features under XeLaTeX and LuaLaTeX.
  6. CTAN: plex — the existing IBM Plex LaTeX distribution and its Type 1 / autoinst approach.
  7. IBM Plex source and releases — upstream fonts and licensing.
  8. fontTools documentation — inspecting cmap, GSUB, GPOS, and other font tables.

TrueType Font in pdfTeX - V: Virtual Fonts, Ligatures, and OpenType
https://zongpingding.github.io/2026/11/14/TrueType_Font_in_pdfTeX_V/
Author
Eureka
Posted on
November 14, 2026
Licensed under