# Why are inline math symbols shifted left when used inside italic text?

If you are familiar with amsthm, you probably know that the default font for theorems is italic. If you put inline math into a theorem (or more generally, inside italic text), it gets somehow shifted to the left with respect to what you would expect if the content was typeset as pure text only.

This is especially noticeable for isolated capital letters like $U$, and in my opinion this can be visually displeasing. Is there a way to fix this behavior (other than writing the symbols as non-math letters)? Maybe with some kind of italic (un)correction?

Here is a very minimal working example:

\documentclass{article}

\usepackage{amsmath,amsthm}

\newtheorem{theorem}{Theorem}

\begin{document}

\begin{theorem}
Consider the following:

Let $U$ and $V$ be open sets (math mode).

Let U and V be open sets (pure text).
\end{theorem}

\end{document}


As you can see in the output, the $U$ and $V$ letters are shifted to the left with respect to their pure text equivalent (you can zoom on the image if it is too small to see the offset). As a result, there is a large gap after them, which in my opinion looks quite ugly.

• Potential duplicate: Bad spacing of math letters within italic text – barbara beeton Oct 24 '19 at 1:09
• – campa Oct 24 '19 at 7:31
• Thank you. I must say that I spent quite some time looking for a duplicate on the site but did not find those. Sadly, as far as I can tell, the conclusion of these discussions is that there is no easy fix for the issue. – Vincent Forman Oct 24 '19 at 8:23

I was very frustrated not to be able to fix the issue with CM font, because I use amsthm often and I like the plain theorem style and want their bodies to be italic. So I tried to improve the "crazy solution" proposed by Hendrik Vogt in Bad spacing of math letters within italic text. Below is a working example, which relies on defining a new theorem style: fixedplain.

If you want to use the code below to fix the position of math letters when using AMS plain theorem style, please note the following:

• you need to put what's between "start of patch" and "end of patch" after \usepackage{amsthm} and before you use \newtheorem
• it will only affect the theorem bodies
• it will try to figure out if inline math enclosed with $...$ starts or ends with a letter, and apply some horizontal skips to mimic the usual text flow of italic text, thus removing the unwanted shift to the left you usually get;
• if will try to mimic the behavior of the default font (cmr10), and will probably end up with wrong offsets for other fonts (although it could be more or less correct if you only change the font size). However, see below how you can generate an offset table for other fonts;
• it will improperly detect e.g. that $2^n$ ends with a letter, but you can fix it by using $2^{n}$ instead;
• you can still get the standard behavior if needed, by using $$...$$;
• it works by making $ active. As a consequence, it will fail: • whenever you use $$...$$ for display math (but this is considered bad practice and you should avoid using it anyway, see Why is $$ preferable to $$…$$?) • it will fail, possibly with a compile error, if you imbricate $'s, which can happen if you need math inside a \text{...} (still, I cannot figure a reasonable way that would cause a compile error with valid inline math input). But in that case, you could use $$ and $$ for the interior math, at least to avoid the error.
\documentclass{article}

\usepackage{amsthm,xcolor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Start of patch
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\makeatletter

\def\fix@mchar#1#2#3{%
\expandafter\edef\csname mcl@#1\endcsname{#2}%
\expandafter\edef\csname mcr@#1\endcsname{#3}%
}

\fix@mchar a{+1.168}{+0.972}\fix@mchar b{+1.199}{+0.319}\fix@mchar c{+1.162}{+0.397}
\fix@mchar d{+1.043}{+0.862}\fix@mchar e{+1.156}{+0.890}\fix@mchar f{-1.500}{+3.336}
\fix@mchar g{+0.664}{+1.127}\fix@mchar h{+0.362}{+1.194}\fix@mchar i{+0.837}{+1.250}
\fix@mchar j{-0.534}{+2.108}\fix@mchar k{+0.362}{+1.603}\fix@mchar l{+0.866}{+1.837}
\fix@mchar m{+0.863}{+1.001}\fix@mchar n{+0.863}{+0.970}\fix@mchar o{+1.158}{+0.246}
\fix@mchar p{+0.832}{+0.381}\fix@mchar q{+1.073}{+1.163}\fix@mchar r{+0.863}{+1.662}
\fix@mchar s{+0.325}{+1.037}\fix@mchar t{+0.992}{+1.401}\fix@mchar u{+0.849}{+0.904}
\fix@mchar v{+0.847}{+1.479}\fix@mchar w{+0.853}{+1.529}\fix@mchar x{+0.351}{+1.817}
\fix@mchar y{+0.826}{+1.100}\fix@mchar z{+0.230}{+1.705}\fix@mchar A{+0.132}{-0.123}
\fix@mchar B{+0.274}{+1.419}\fix@mchar C{+1.662}{+2.411}\fix@mchar D{+0.270}{+1.207}
\fix@mchar E{+0.299}{+1.712}\fix@mchar F{+0.299}{+2.078}\fix@mchar G{+1.662}{+1.334}
\fix@mchar H{+0.277}{+2.528}\fix@mchar I{+0.300}{+2.346}\fix@mchar J{+0.087}{+2.076}
\fix@mchar K{+0.280}{+2.267}\fix@mchar L{+0.277}{+0.880}\fix@mchar M{+0.301}{+2.686}
\fix@mchar N{+0.284}{+2.536}\fix@mchar O{+1.662}{+1.434}\fix@mchar P{+0.270}{+1.669}
\fix@mchar Q{+1.541}{+1.406}\fix@mchar R{+0.252}{+0.272}\fix@mchar S{+0.322}{+1.715}
\fix@mchar T{+2.385}{+2.169}\fix@mchar U{+2.377}{+2.649}\fix@mchar V{+2.417}{+3.155}
\fix@mchar W{+2.291}{+2.885}\fix@mchar X{+0.167}{+2.666}\fix@mchar Y{+2.617}{+3.323}
\fix@mchar Z{+0.254}{+2.329}

\def\get@lastchar#1#2${% \if\relax#2\relax% #1% \else% \if#2 % #2% \else \get@lastchar#2$%
\fi%
\fi%
}

\def\get@firstchar#1#2${#1} \let\math@org=$

\newlength\mylength

\def\itinlinemath#1{%
\edef\leftskip@csname{mcl@\expandafter\get@firstchar\detokenize{#1}$}% \edef\rightskip@csname{mcr@\expandafter\get@lastchar\detokenize{#1}$}%
\math@org%
\ifcsname\leftskip@csname\endcsname%
\null\mskip\csname\leftskip@csname\endcsname mu%
\fi%
#1%
\ifcsname\rightskip@csname\endcsname%
\mskip-\csname\rightskip@csname\endcsname mu%
\fi%
\math@org%
}

\begingroup
\catcode\$=13 \gdef\activateitalicmath{% \catcode\$=13%
\def${\math@org}% \def${\math@org}%
\def$##1${\itinlinemath{##1}}%
}
\endgroup

\newtheoremstyle{fixedplain}{\topsep}{\topsep}{\itshape\activateitalicmath}{0pt}{\bfseries}{.}{5pt plus 1pt minus 1pt}{}

%Remove the following line to disable patch and restore default behavior
\theoremstyle{fixedplain}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% End of patch
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newtheorem{theorem}{Theorem}

\begin{document}

\begin{theorem}[Single letters]
If $U$ or $V$ and $X$, and $f$ from $j$. Let $T$ be $S$ if $Y$.
\end{theorem}

\begin{theorem}[Unaffected math]
If $2U^2$ or $+V_1$ and $-X\alpha$, and $\frac f2$ from $2+j^{x}$. Let $\overline{T}$ be ${{S}}$ if $$Y$$.
\end{theorem}

\end{document}


Here is the result (with the default behavior below).

Here is a test of various scenario.

To "mimic" the behavior of the italic font and generate an offset table for letters, here is the LaTeX code I have been using.

\documentclass{article}

\usepackage[paperheight=40cm,paperwidth=5cm,margin=0pt]{geometry}

\pagestyle{empty}

\def\refrule{\raisebox{-1ex}{\rule{2em}{4ex}}}

\def\glyph#1{%
\par%
\noindent%
{\refrule} #1 {\refrule} $#1$ {\refrule}%
}

\def\multiglyph #1#2.{
\glyph#1%
\ifx&#2&%
\else%
\multiglyph#2.
\fi%
}

\begin{document}

\raggedright\itshape\multiglyph abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.

\end{document}


This file is then compiled and converted to png image (using imagemagick) by the following python script, which will then compute approximate left and right offsets for each character. It is quite experimental, but at least it does work for cmr10 and seems to give consistent results. If you want to use another font, simply change the beginning of the above file to load your font, and relaunch the python script.

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import subprocess

def open_image(path):
i = Image.open(path).convert('L')
a = np.frombuffer(i.tobytes(), np.uint8).astype(np.float32)
return a.reshape((i.size[1], i.size[0]))

subprocess.run("pdflatex glyphs.tex", check = True, shell = True)
subprocess.run("convert -density 1000 glyphs.pdf glyphs.png", check = True, shell = True)

image = open_image("glyphs.png")
image[:, 0] = 255

glyph_centers = []
rule_width, rule_height = 0.0, 0
in_glyph = False
for k in range(image.shape[0]):
bounds = np.where(np.diff(image[k]))[0]
glyph_found = len(bounds) > 5
if in_glyph ^ glyph_found:
if glyph_found:
glyph_top = k
left_before, left_after, left_height, right_before, right_after, right_height = 0.0, 0.0, 0, 0.0, 0.0, 0
else:
left_before /= left_height
left_after /= left_height
right_before /= right_height
right_after /= right_height
glyph_centers.append([left_before, left_after, right_before, right_after])
in_glyph = glyph_found
if in_glyph:
lengths = (bounds - np.roll(bounds, 1))[1::2]
rules = np.sort(np.argpartition(lengths, -3)[-3:])
rule_width += np.sum(bounds[rules * 2 + 1] - bounds[rules * 2])
rule_height += 1
a, b = bounds[rules[0] * 2 + 2], bounds[rules[1] * 2 - 1]
image[k, a + 1: b + 1] = 127
if b > a:
left_before += a - bounds[rules[0] * 2 + 1]
left_after += bounds[rules[1] * 2] - b
left_height += 1
a, b = bounds[rules[1] * 2 + 2], bounds[rules[2] * 2 - 1]
image[k, a + 1: b + 1] = 127
if b > a:
right_before += a - bounds[rules[1] * 2 + 1]
right_after += bounds[rules[2] * 2] - b
right_height += 1
rule_width = rule_width / (3 * rule_height)
assert(len(glyph_centers) == 52)
k = 0
ruletoem = 2 # Rule is 2 em large
emtomu = 18  # 1em -> 18 mu

f = open("cmr10.fix","wt") #Change file name as needed
for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
delta_before = (glyph_centers[k][0] - glyph_centers[k][2]) / rule_width * ruletoem * emtomu
delta_after = (glyph_centers[k][3] - glyph_centers[k][1]) / rule_width * ruletoem * emtomu
s = "\\fix@mchar {:}{{{:+.3f}}}{{{:+.3f}}}\n".format(c, delta_before, delta_after)
f.write(s)
k += 1
f.close()