为什么内联数学符号在斜体文本中使用时会向左移动?

为什么内联数学符号在斜体文本中使用时会向左移动?

如果您熟悉amsthm,您可能知道定理的默认字体是斜体。如果您将内联数学放入定理中(或者更一般地,放入斜体文本中),它会以某种方式向左移动,这与内容仅排版为纯文本时的情况不同。

对于单独的大写字母(如)来说$U$,这种情况尤其明显,在我看来,这在视觉上令人不悦。有没有办法修复此行为(除了将符号写成非数学字母)?也许使用某种斜体(非)校正?

这是一个非常简单的工作示例:

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

正如您在输出中看到的那样,$U$$V$字母相对于纯文本向左移动(如果图像太小而看不到偏移,您可以放大图像)。结果,它们后面有一个很大的间隙,在我看来看起来相当丑陋。

问题

答案1

我很沮丧,无法修复 CM 字体的问题,因为我amsthm经常使用,而且我喜欢普通的定理风格,希望它们的正文是斜体的。所以我尝试改进由亨德里克·沃格特斜体文本中的数学字母间距错误。下面是一个工作示例,它依赖于定义一个新的定理样式:fixedplain

如果您想在使用 AMS 普通定理样式时使用以下代码来固定数学字母的位置,请注意以下几点:

  • \usepackage{amsthm}你需要在使用之前和之后放置“补丁开始”和“补丁结束”之间的内容\newtheorem
  • 它只会影响定理主体
  • 它将尝试确定内联数学符号是否以$...$字母开始或结束,并应用一些水平跳过来模拟斜体文本的通常文本流,从而消除通常会出现的向左不必要的移动;
  • 它将尝试模仿默认字体 (cmr10) 的行为,并且可能最终导致其他字体的偏移错误(尽管如果您仅更改字体大小,它或多或少可能是正确的)。但是,请参见下文如何为其他字体生成偏移表;
  • 它会错误地检测$2^n$以字母结尾的例如,但你可以通过使用来修复它$2^{n}$
  • 如果需要,您仍然可以通过使用来获得标准行为\(...\)
  • 它通过$激活来工作。因此,它将失败:
    • 每当你使用$$...$$显示数学时(但这被认为是不好的做法,你应该避免使用它,请参阅为什么\[\]比$$…$$更好?
    • 如果您将 重叠起来,则可能会失败,并可能出现编译错误,$如果您需要在 内部进行数学运算,则可能会发生这种情况\text{...}(尽管如此,我还是无法找到一种合理的方法,会导致有效的内联数学输入导致编译错误)。但在这种情况下,您可以使用\(\)进行内部数学运算,至少可以避免错误。
\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}

这是结果(采用下面的默认行为)。

定理检验

以下是各种场景的测试。

更多测试

为了“模仿”斜体字体的行为并生成字母的偏移表,这是我一直在使用的 LaTeX 代码。

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

然后,该文件被以下 Python 脚本编译并转换为 png 图像(使用 imagemagick),然后该脚本将计算每个字符的近似左右偏移量。它只是实验性的,但至少它对 cmr10 有效,并且似乎能给出一致的结果。如果您想使用其他字体,只需更改上述文件的开头以加载您的字体,然后重新启动 Python 脚本。

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()

相关内容