Make a Thicker Latin Modern Roman
All free fonts with unicode-math support are imperfect. To my taste, Latin Modern is the most polished free OTF font with unicode math, except for its thin and sharp strokes and the lack of lowercase script math. I spent some hobby time trying to figure out how to automatically create a thicker version of the font.
2019-11-02 2020-02-11

# Why Latin Modern

As mentioned in the previous post, there are multiply reasons to migrate to XeTeX and LuaTeX. In particular, the possibility to easily use OpenType fonts is such a good feature that I would never look back to the classical TeX if I could choose.

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
All of them are imperfect. Here are some points one can easily realize:
• Both STIX version 1 and 2 are beautiful fonts for the text part, however
• XITS/STIX1 do not distinguish bold and regular uppercase letters obviously.
• STIX Two is not mature enough. Especially, the math font has misaligned accents. Besides it contains a few annoying mistakes which require special tricks to amend.
• 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 and regular styles of `v` and `w` is not obvious.
• I did not try Bonum for documents. Its glyphs are slightly wider than the others and may cause overfull boxes, especially when it is used in A5 pages.
• The strokes in Latin Modern/Computer Modern fonts are too thin for screens and laser printers. (Though the original CMR could be acceptable, if one could perform a few tests on the target printer, but still not optimal for screen reading.)
Latin Modern (LM) is in my taste the most polished font in this list except the thinness and the lack of lowercase script letters. Hence, I started to figure out an automatic way to increase its thickness. The results are not professional, but they saved my eyes when reading on the monitors and are printer-friendly.

# Excluded Solutions

## The Original Computer Modern

In T3 computer modern, one can adjust the `blacker` value to make the glyphs thicker. However, the texts are rendered as if they were misaligned, even with a high rasterizing DPI. Besides, who would use a bitmap font nowadays?

## To Rebuild CM-Super or Latin Modern

Rebuilding cm-super from a blacker T3 CM bitmaps does not seem to be a time-saving solution. On the other hand, the sources of LM Roman and its math symbols are not completely obtainable. This approach is excluded.

# Solution 1: Fakebold by Fontspec

TL; DR: The visual effect depends on the PDF reader software

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 looking 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 (full bold).

# Solution 2: Modify the OTF

A shell script is made to perform the work. The folder structure is as follows:
```(my doc folder)\$ tree fakebold/
fakebold/
└── 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 files
```
Although 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.

## Approach a: Increasing Weight

This approach works well on the text fonts. However, I could not find a way to tune the weight of math font without triggering the warning of (only) Adobe PDF reader. The script calls `ChangeWeight()` of fontforge. After the weight is enlarged, each glyph becomes slightly wider. Therefore, a rescaling is worth to be considered. Only horizontal shrinking is applied here since vertical scaling will misalign the baselines.

There are two caveats:

1. Simplifying the glyphs before scaling, otherwise the shapes of some symbols will be destroyed, as visualized in Fig.1a-1c.

Fig. 1a:the original `multiply` symbol

Fig. 1b:directly increasing the weight by 15

Fig. 1c:increasing weight by 15 after simplification
2. Some glyphs have distorted shapes after changing the weight, as shown in the Fig.2.
Fig. 2:Left side is the original glyph, right side is the one with an increased weight
The solution is to split the increasing of weight into sub-steps accumulatively.
The `adjust-lm-weight.pe` for this approach 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
```

## Approach b: Expanding Stroke

This might be the best approach so far. Expanding the strokes does not only work for text symbols, but also for the math. This approach does not have the first issue of increasing weight, but it is still bothered by the second issue, which can be solved in the same way.

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 wait
```
Hints should be removed, as the original ones are no more optimal and `AutoHint()` from fontforge is not clever enough.

## Settings for fontspec

``` 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}
```

# Known Problems

• No hints, but this is with the modern PDF renders no more a critical problem.
• Sans Serif Math symbols are 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).