Make a Thicker Latin Modern Roman

All free fonts with unicode-math support are imperfect. For my taste, Latin Modern is the most polished free OTF font with unicode math, except its thin and sharp strokes for the "modern" laser printers and the lack of lowercase script math. I spent a few nights to figure out how to automatically create a thicker version of the font.

Date of posting: 2019-11-02
Last modification: 2020-02-11

The following fonts are the available ones with unicode-math support on my operating system:

- XITS Math
- STIX Two Math
- Asana Math
- TeX Gyre Bonum Math
- TeX Gyre Pagella Math
- TeX Gyre Schola Math
- TeX Gyre Termes Math
- TeX Gyre DejaVu Math
- Latin Modern Math
- Libertinus Math
- Garamond Math
- Cambria Math

- Both STIX version 1 and 2 are basically very beautiful fonts for the text, but
- XITS/STIX1 does not distinguish the bold and regular uppercase letters obviously.
- STIX Two is still not mature enough. Especially, the Math font has misaligned accents. Plus some other mistakes, which sometimes appear and I have to figure out special tricks to solve them.

- Schola and Termes has the same non-optimal kerning for mixed italic and
upright styles. For example, both much too less space before a bold
italic
`j`

. - Pagella has comfortable kernings, but the difference between bold
variant and regular
`v`

,`w`

are not clear enough. - I have not used Bonum for documents, since the glyphs are slightly wider than the others, they may cause more Overfull boxes, especially when that is used in A5 pages. Bless Microtype will save you.
- The sharp and thin strokes in the Latin/Computer Modern Roman fonts was the reason, that I have never applied CM-Super to any of my document. (The original CMR might be OK, if one could perform a few tests on the target printer, but definitively not optimal for the screen reading.)

`blacker`

value to
make the glyphs thicker. However, that will not be well displayed by a low-DPI
monitor. Typically the T3 texts would be rendered as if they were misaligned,
even with a high rasterizing DPI. Besides, who would use a bitmap font nowadays?
The fake bold works in both XeLaTeX and LuaLaTeX. In XeLaTeX it is written as

\setmainfont[% FakeBold=1, SmallCapsFont={* Caps}, SlantedFont={* Slanted}, ]{Latin Modern Roman}and for the current LuaLaTeX (works in XeLaTeX as well)

\setmainfont[% RawFeature={embolden=1.0} SmallCapsFont={* Caps}, SlantedFont={* Slanted}, ]{Latin Modern Roman}A fakebold factor of 1.0 as in the above settings makes the document look quite nice in Adobe Reader, SumatraPDF and in the Firefox browser, too. However, the visual effect depends on the PDF reader. Okular renders fakebolds imperfectly, but still acceptable; while the current evince or other readers based on poppler cairo will make the font much bolder, which becomes full bolds. Hence, this solution works only for limited PDF readers.

(my doc folder)$ tree fakebold/ fakebold/ ├── adjust-lm.sh ├── adjust-lm-stroke.pe ├── adjust-lm-weight.pe └── origin ├── latinmodern-math.otf ├── lmroman10-bolditalic.otf ├── lmroman10-bold.otf ├── lmroman10-italic.otf ├── lmroman10-regular.otf ├── lmroman12-bold.otf ├── lmroman12-italic.otf ├── lmroman12-regular.otf ├── lmroman17-regular.otf ├── lmroman5-bold.otf ├── lmroman5-regular.otf ├── lmroman6-bold.otf ├── lmroman6-regular.otf ├── lmroman7-bold.otf ├── lmroman7-italic.otf ├── lmroman7-regular.otf ├── lmroman8-bold.otf ├── lmroman8-italic.otf ├── lmroman8-regular.otf ├── lmroman9-bold.otf ├── lmroman9-italic.otf ├── lmroman9-regular.otf ├── lmromancaps10-oblique.otf ├── lmromancaps10-regular.otf ├── lmromandunh10-oblique.otf ├── lmromandunh10-regular.otf ├── lmromanslant10-bold.otf ├── lmromanslant10-regular.otf ├── lmromanslant12-regular.otf ├── lmromanslant17-regular.otf ├── lmromanslant8-regular.otf ├── lmromanslant9-regular.otf └── lmromanunsl10-regular.otf 1 directory, 37 filesAlthough the Python API of fontforge exposures more features than the native script language, those additional features will not be needed here. In addition, the advantage of the native script is that the work can be easily parallelized by launching a shell process for each OTF file as you will see later.

`ChangeWeight()`

function of
fontforge. After the weight is enlarged, each glyph also becomes slightly
wider. Therefore, a rescaling is worth to be considered. Since the vertical
scaling will misalign the baseline of the letters, a horizontal shrinking is
applied here.
There are two additional points to be taken into account:

- One needs to simplify the glyphs before scaling, otherwise, the shapes
of some symbols like will be destroyed, as visualized in the follow
three figures
- Some glyphs will have distorted shapes after changing the weight, as shown in the next figure. The solution is to accumulatively increase the weight step by step, at which this distortion does not appear.

`adjust-lm-weight.pe`

is
1 #!/user/bin/fontforge 2 3 Open("origin/"+$1) 4 SelectAll() 5 Simplify() 6 i = 0 7 while(i < Strtol($2)) 8 ChangeWeight(Strtod($3)) 9 ++i 10 endloop 11 if($4 == "true") 12 Scale(95, 100) 13 endif 14 RemoveOverlap() 15 RemoveHints() 16 Generate($1) 17 Close()and

`adjust-lm-weight.sh`

is:
1 #!/bin/sh 2 3 for i in origin/lmroman*.otf ; do 4 fontforge adjust-lm.pe "${i#origin/}" 3 5.0 true & 5 done 6 7 fontforge adjust-lm.pe latinmodern-math.otf 1 15.0 false & 8 9 wait

The script `adjust-lm-stroke.pe`

is now

1 #!/user/bin/fontforge 2 3 Open("origin/"+$1) 4 SelectAll() 5 joinstyle = 0 6 i = 0 7 while (i < Strtod($2)) 8 ExpandStroke(Strtol($3), 1, joinstyle, 0, 1) 9 ++i 10 endloop 11 RemoveOverlap() 12 ClearHints() 13 Simplify() 14 RoundToInt() 15 Generate($1) 16 Close()and

`adjust-lm-weight.sh`

:
1 #!/bin/sh 2 for i in origin/lmroman*.otf ; do 3 fontforge adjust-lm-stroke.pe "${i#origin/}" 3 6 & 4 done 5 # \symscr{L} would have problem with 3*6, switch to 1*18 for math 6 fontforge adjust-lm-stroke.pe latinmodern-math.otf 1 18 & 7 waitThe hints should be removed, as the original ones are no more optimal and

`AutoHint()`

from fontforge is not clever enough.
1 % Extract the fonts in 'fakebold/' 2 \usepackage{fontspec} 3 \usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} 4 \setmainfont[% 5 Path={fakebold/}, 6 % 7 UprightFont={*-regular.otf}, 8 UprightFeatures={% 9 SmallCapsFont={lmromancaps10-regular.otf}, 10 SizeFeatures={% 11 {Size= -5.5, Font={lmroman5-regular.otf}},% 12 {Size= 5.5-6.5, Font={lmroman6-regular.otf}},% 13 {Size= 6.5-7.5, Font={lmroman7-regular.otf}},% 14 {Size= 7.5-8.5, Font={lmroman8-regular.otf}},% 15 {Size= 8.5-9.5, Font={lmroman9-regular.otf}},% 16 {Size= 9.5-11.5, Font={lmroman10-regular.otf}},% 17 {Size= 11.5-14.5, Font={lmroman12-regular.otf}},% 18 {Size= 14.5- , Font={lmroman17-regular.otf}}% 19 }% 20 },% 21 % 22 BoldFont={lmroman10-bold.otf}, 23 BoldFeatures={% 24 SizeFeatures={% 25 {Size= -5.5, Font={lmroman5-bold.otf}},% 26 {Size= 5.5-6.5, Font={lmroman6-bold.otf}},% 27 {Size= 6.5-7.5, Font={lmroman7-bold.otf}},% 28 {Size= 7.5-8.5, Font={lmroman8-bold.otf}},% 29 {Size= 8.5-9.5, Font={lmroman9-bold.otf}},% 30 {Size= 9.5-11.5, Font={lmroman10-bold.otf}},% 31 {Size= 11.5- , Font={lmroman12-bold.otf}},% 32 }% 33 },% 34 % 35 ItalicFont={lmroman10-italic.otf}, 36 ItalicFeatures={% 37 SizeFeatures={% 38 {Size= -7.5, Font={lmroman7-italic.otf}},% 39 {Size= 7.5-8.5, Font={lmroman8-italic.otf}},% 40 {Size= 8.5-9.5, Font={lmroman9-italic.otf}},% 41 {Size= 9.5-11.5, Font={lmroman10-italic.otf}},% 42 {Size= 11.5- , Font={lmroman12-italic.otf}}% 43 }% 44 },% 45 BoldItalicFont={lmroman10-bolditalic.otf},% 46 % 47 SlantedFont={lmromanslant10-regular.otf}, 48 SlantedFeatures={% 49 SmallCapsFont={lmromancaps10-oblique.otf}, 50 SizeFeatures={% 51 {Size= -8.5, Font={lmromanslant8-regular.otf}},% 52 {Size= 8.5-9.5, Font={lmromanslant9-regular.otf}},% 53 {Size= 9.5-11.5, Font={lmromanslant10-regular.otf}},% 54 {Size= 11.5-14.5, Font={lmromanslant12-regular.otf}},% 55 {Size= 14.5- , Font={lmromanslant17-regular.otf}}% 56 }% 57 },% 58 BoldSlantedFont={lmromanslant10-bold.otf},% 59 % 60 Ligatures={TeX} 61 ]{lmroman10}% 62 \newfontfamily\upit[Path={fakebold/}]{lmromanunsl10-regular.otf} 63 \newcommand{\textupit}[1]{{\upit#1}} 64 \newcommand{\textitup}[1]{{\upit#1}} 65 \newfontfamily\dhstyle[Path={fakebold/},Scale=MatchLowercase,UprightFont={*-regular.otf},SlantedFont={*-oblique.otf}]{lmromandunh10} 66 \newcommand{\textdh}[1]{{\dhstyle#1}} 67 % 68 \setsansfont[% 69 Scale=MatchUppercase, 70 Ligatures=TeX 71 ]{Latin Modern Sans} 72 \newfontfamily{\dcstyle}[% 73 ItalicFont={lmsansdemicond10-oblique.otf},% 74 SlantedFont={lmsansdemicond10-oblique.otf},% 75 ]{lmsansdemicond10-regular.otf} 76 \newcommand{\textdc}[1]{{\dcstyle#1}} 77 % 78 \setmonofont[% 79 Scale=MatchUppercase 80 ]{Latin Modern Mono} 81 % 82 \setmathfont[% 83 Path={fakebold/}, 84 bold-style=ISO, 85 partial=upright 86 ]{latinmodern-math.otf} 87 % 88 \setmathfontface\symupit[% 89 Path={fakebold/}, 90 ]{lmromanunsl10-regular.otf} 91 % 92 \setmathfontface\symitup[% 93 Path={fakebold/}, 94 ]{lmromanunsl10-regular.otf}

- Hints are not manually tuned, but this is with the modern PDF renders no more a critical problem.
- Sans Serif Math symbols also become improperly thicker (but acceptable).
~~Fraktur letters become improperly thicker~~(fixed in the recent version).- The thickness of LM Sans and other fonts have to be rematched (but acceptable by default).

*Edit 2020-02-21:* update download link, update sample TeX settings.