The letterswitharrows package
Max Teegen tex@jmteegen.eu Released 2021-07-19
This package provides math-mode commands for setting left and right arrows over mathematical symbols, so that the arrows dynamically scale with the symbols. Here is a sample:
s ≤ t ∈ U
rAB = AB A ≬ B
While it is possible to set arrows over longer strings of symbols, the focus lies on single characters.
Only pdf output is supported. Output to ps is implemented, but rarely tested. For a wider range of formats there is pgf-based output.
1 Usage
The package provides the general-purpose \arrowoverset command, as well as some sets of predefined shorthand commands.
1.1 Presets
The presets are selected by passing them as options to the presets package option. For instance, to define the abc and the vec-cev sets of commands you would load the package like so:
\usepackage[presets={abc,vec-cev}]{letterswitharrows}
By default, the abc, ABC and cAcBcC presets are loaded.
Passing abc to the presets option allows you to use the \v⟨char ⟩ and \⟨char ⟩v abc
commands for all the lower-case letters a through z except for v.
For the letter v the commands \vleft and \vright are provided.
a, b, c, d, m, F
t\[ \va, \vb, \vc, \dv, \mv, F_\tv \]
\v<char>
\<char>v
\vleft
\vright
Passing ABC to the presets option allows you to use the \v⟨CHAR ⟩ and \⟨CHAR ⟩v ABC
commands for all the upper-case letters A through Z.
A, B , C , D, E , F
G\[ \vA, \vB, \vC, \Dv, \Ev, F_\Gv \]
\v<CHAR>
\<CHAR>v
Passing cAcBcC to the presets option allows you to use the \vc⟨CHAR ⟩ and cAcBcC
\c⟨CHAR ⟩v commands for all the upper-case letters A through Z to set arrows over
\mathcal-letters.
A, B, C , D, E , F
G\[ \vcA, \vcB, \vcC, \cDv, \cEv, F_\cGv \]
\vc<CHAR>
\c<CHAR>v
Passing vec-cev to the presets option (re)defines the \vec and \cev commands.
vec-cev
Unlike the other commands these do not automatically consume subsequent subscripts or ’ tokens.
x := AB ⟨w, v⟩ = 42
\[ \vec{\mathbf{x}} := \cev{AB} \qquad \langle \vw, \vright \rangle = 42 \]
\vec
\cev
1.2 The \arrowoverset command
\arrowoverset [⟨xoffset ⟩] [⟨xscale ⟩] [⟨yoffset ⟩] {⟨math ⟩}
\arrowoverset* [⟨xoffset ⟩] [⟨xscale ⟩] [⟨yoffset ⟩] {⟨math ⟩}
This command sets a right (or left if \arrowoverset* is used) arrow over ⟨math⟩. The base length of the arrow is the width of the ⟨math⟩ multiplied by ⟨xscale⟩, which must be specified as a fraction ⟨num⟩/⟨denom⟩. The arrow is offset by ⟨xoffset⟩ to the right, which must be a math skip expression, and by ⟨yoffset⟩ to the top, which must be a skip expression.
This command consumes subsequent subscripts or up to two primes ’. The former does not affect the length of the arrow.
\arrowoverset
\arrowoverset*
1.3 Other package options
If you specify the pgf option, every arrow is drawn as a pgfpicture. This requires the pgf
pgf package.
TEXhackers note: You can set up custom arrow drawing code by redefining \__jmt_- lwa_arrow_draw:nnn. The command is expected to draw an arrow with its head at the current position. Its length should be #1 and it should be drawn at a font size of #2pt. If #3 is - if the arrow should point rightwards and empty otherwise.
Specifying linewidth=<value> as a package option allows you to adjust the line linewidth
width of the arrows to adjust for the weigth of the maths font you are using. The default value is linewidth=0.3.
Specifying the tweaks option applies per-letter scaling adjustments to some of the
tweaks
single-letter shorthands. This is enabled by default. These are specific to Latin Modern Math and subject to be changed on a whim. If you wish a more stable behaviour specify tweaks=false. This documentation uses tweaks=false.
2 Implementation
1
\NeedsTeXFormat{LaTeX2e}
2
\RequirePackage{expl3}
3
\ProvidesExplPackage {letterswitharrows} {2021/07/19} {} {Draw arrows over math letters.}
4
\RequirePackage{xparse,l3keys2e,mathtools}
5
% TODO: I just use mathtools for mathrlap; replace.
6
7
⟨@@=jmt_lwa⟩
8
\msg_new:nnn {letterswitharrows} {pdf-only} {Only~pdf~output~is~supported.}
9
\AtBeginDocument{
10
\sys_if_output_pdf:F {
11
\msg_warning:nn {letterswitharrows} {pdf-only}
12
}
13
}
The drawing code.
\__jmt_lwa_arrow_draw_special:nnn
\__jmt_lwa_arrow_draw_pgf:nnn
\__jmt_lwa_arrow_left:nn
\__jmt_lwa_arrow_right:nn
14
\cs_new:Nn \__jmt_lwa_arrow_draw_special:nnn % length, font size, sign
15
{
16
\sys_if_output_pdf:TF {
17
\tex_special:D {pdf:~
18
q~
19
1~J~1~j~
20
1~0~0~\dim_to_decimal:n{#3#2pt/10}~0~0~cm~
21
\fp_use:c{g__jmt_lwa_line_width}~w~
22
q~
23
\dim_to_decimal:n{#3#2pt/10}~0~0~1~0~0~cm~
24
1~0~0~1~-1~0~cm~
25
0~1~m~
26
.25~0~1~0~1~0~c~
27
1~0~.25~0~0~-1~c~
28
S~
29
Q~
30
Q~
31
q~
32
0~0~m~
33
-1~0~0~1~0~0~cm~
34
\fp_use:c{g__jmt_lwa_line_width}~w~
35
\dim_to_decimal:n{#3#1}~0~l~S~
36
Q
37
}
38
} {
39
\tex_special:D {"~
40
1~setlinecap~1~setlinejoin~
41
1~0~0~\dim_to_decimal:n{#3#2pt/10}~0~0~6~array~astore~concat~
42
\fp_use:c{g__jmt_lwa_line_width}~setlinewidth~
43
gsave~
44
\dim_to_decimal:n{#3#2pt/10}~0~0~1~0~0~6~array~astore~concat~
45
1~0~0~1~-1~0~6~array~astore~concat~
46
0~1~moveto~
47
.25~0~1~0~1~0~curveto~
48
1~0~.25~0~0~-1~curveto~
49
stroke~
50
grestore~
51
0~0~moveto~
52
-1~0~0~1~0~0~6~array~astore~concat~
53
\dim_to_decimal:n{#3#1}~0~lineto~stroke
54
}
55
}
56
}
57 58
% TODO
59
% \tl_new:N \g__jmt_lwa_pgf_arrow_style_tl
60
% \tl_set:Nn \g__jmt_lwa_pgf_arrow_style_tl
61
% {Computer~Modern~Rightarrow[width=#2pt*2/10,length=#2pt/10,sharp]}
62
63
\cs_new:Nn \__jmt_lwa_arrow_draw_pgf:nnn {
64
\begin{pgfpicture}
65
\pgfsetlinewidth{#2pt*\fp_use:c{g__jmt_lwa_line_width}/10}
66
\pgfsetarrowsstart
67
{Computer~Modern~Rightarrow[width=#2pt*2/10,length=#2pt/10,sharp]}
68
% \pgfsetarrowsstart{\tl_use:N \g__jmt_lwa_pgf_arrow_style_tl}
69
\pgfpathmoveto{\pgfpointorigin}
70
\pgfpathlineto{\pgfpoint{-#3#1}{0cm}}
71
\pgfusepath{stroke}
72
\pgfresetboundingbox
73
\end{pgfpicture}
74
}
75
76
\cs_new_eq:NN \__jmt_lwa_arrow_draw:nnn \use_none:nnn
77
78
\cs_new:Nn \__jmt_lwa_arrow_right:nn {
79
\skip_horizontal:n {#1}
80
% \rule[\dimexpr -#2pt/6\relax]{#1}{\dimexpr #2pt/3\relax}
81
\__jmt_lwa_arrow_draw:nnn {#1} {#2} {}
82
}
83
84
\cs_new:Nn \__jmt_lwa_arrow_left:nn {
85
\__jmt_lwa_arrow_draw:nnn {#1} {#2} {-}
86
\skip_horizontal:n {#1}
87
% \rule[\dimexpr -#2pt/6\relax]{#1}{\dimexpr #2pt/3\relax}
88
}
(End definition for \__jmt_lwa_arrow_draw_special:nnn and others.)
The core functions.
\__jmt_lwa_arrow_overset_style:Nnncnnn
\__jmt_lwa_arrow_overset:nnnnn
89\cs_new:Npn \__jmt_lwa_arrow_overset_style:Nnncnnn #1#2#3#4#5#6#7 {
90
\hbox_set:Nn \l_tmpa_box {$\m@th#1#3$}
91
\dim_set:Nn \l_tmpa_dim {#2 pt/10}
92
\vbox:n {
93
\tex_lineskiplimit:D = \maxdimen
94
\tex_baselineskip:D = 0pt
95
\tex_tabskip:D = 0pt
96
\tex_lineskip:D = \dim_eval:n {\l_tmpa_dim * 3/2 + #7}
97
\tex_halign:D { ## \tex_cr:D
98
\skip_horizontal:n {\l_tmpa_dim / 2}
99
$
100
\m@th
101
#1
102
\tex_mskip:D \muskip_eval:n {#5}
103
\use:c {#4} {\dim_eval:n{\box_wd:N \l_tmpa_box * #6}} {#2}
104
$
105
\tex_cr:D
106
\box_use_drop:N \l_tmpa_box
107
\tex_cr:D
108
}
109
}
110
}
111
112
\cs_new:Nn \__jmt_lwa_arrow_overset:nnnnn { % content, direction, xoffset, scale, yoffset
113
\mathchoice {
114
\__jmt_lwa_arrow_overset_style:Nnncnnn
115
\displaystyle {\tf@size} {#1} {__jmt_lwa_arrow_#2:nn} {#3} {#4} {#5}
116
} {
117
\__jmt_lwa_arrow_overset_style:Nnncnnn
118
\textstyle {\tf@size} {#1} {__jmt_lwa_arrow_#2:nn} {#3} {#4} {#5}
119
} {
120
\__jmt_lwa_arrow_overset_style:Nnncnnn
121
\scriptstyle {\sf@size} {#1} {__jmt_lwa_arrow_#2:nn} {#3} {#4} {#5}
122
} {
123
\__jmt_lwa_arrow_overset_style:Nnncnnn
124
\scriptscriptstyle {\ssf@size} {#1} {__jmt_lwa_arrow_#2:nn} {#3} {#4} {#5}
125
}
126
}
(End definition for \__jmt_lwa_arrow_overset_style:Nnncnnn and \__jmt_lwa_arrow_overset:nnnnn.)
\__jmt_lwa_arrow_overset:w
\arrowoverset
127\cs_new_protected:Npn \__jmt_lwa_arrow_overset:w {
128
\c_group_begin_token
129
\__jmt_lwa_arrow_overset_aux:w
130
}
131
132
\cs_new:Nn \__jmt_lwa_bool_convert:n {
133
\IfBooleanTF {#1} {\c_true_bool} {\c_false_bool}
134
}
135
136
% This exp_args is necessary because _ generates the wrong token in expl3 syntax
137
\exp_args:NNx \NewDocumentCommand \__jmt_lwa_arrow_overset_aux:w
138
{s O{0mu} O{1} O{0ex} m t’ e{\char_generate:nn {95}{8}} t’} {
139
\__jmt_lwa_arrow_overset:nnnnn
140
{
141
#5
142
\exp_args:Nf\bool_if:nT{\__jmt_lwa_bool_convert:n{#6} || \__jmt_lwa_bool_convert:n{#8}} {
143
\c_math_superscript_token {
144
\scriptscriptstyle\IfBooleanT{#6}{\prime}\IfBooleanT{#8}{\prime}
145
}
146
} % TODO: Better positioning etc?
147
\exp_args:Nf\IfValueT{\use:n#7} {
148
\c_math_subscript_token {
149
\mathrlap{#7}
150
}
151
}
152
}
153
{\IfBooleanTF{#1}{left}{right}}
154
{#2} {#3} {#4}
155
156
\exp_args:Nf\IfValueTF{\use:n#7}{
157
% TODO: Better way to do this? This is all kinds of wrong.
158
\hphantom{\c_math_subscript_token{#7}}
159
} {}
160
\c_group_end_token
161
}
162
\cs_set_eq:NN \arrowoverset \__jmt_lwa_arrow_overset:w Replacements for hyperref bookmarks.
163
\AtBeginDocument{
164
\@ifpackageloaded{hyperref}{
165
\pdfstringdefDisableCommands{
166
% Why does this only work with Expandable?
167
\DeclareExpandableDocumentCommand \__jmt_lwa_arrow_overset:w {s o o o m} {
168
\ifpdfstringunicode
169
{#5 \IfBooleanTF{#1}{\unichar{"20D6}}{\unichar{"20D7}}}
170
{#5}
171
}
172
}
173
}{}
174
}
(End definition for \__jmt_lwa_arrow_overset:w and \arrowoverset. This function is documented on page2.)
Package option handling.
\g__jmt_lwa_tweak_shortcuts_bool
\g__jmt_lwa_selected_presets_prop
\__jmt_lwa_arrow_draw:nnn
175
\bool_new:N \g__jmt_lwa_tweak_shortcuts_bool
176
\prop_new:N \g__jmt_lwa_selected_presets_prop
177
\keys_define:nn {letterswitharrows} {
178
mode .choice:,
179
mode / special .code:n = {
180
\cs_set_eq:NN \__jmt_lwa_arrow_draw:nnn \__jmt_lwa_arrow_draw_special:nnn
181
},
182
mode / pgf .code:n = {
183
\RequirePackage{pgf}
184
\ExplSyntaxOff\usepgflibrary{arrows.meta}\ExplSyntaxOn
185
\cs_set_eq:NN \__jmt_lwa_arrow_draw:nnn \__jmt_lwa_arrow_draw_pgf:nnn
186
},
187
mode .initial:n = {special},
188
pgf .meta:n = {mode = pgf},
189
presets .multichoices:nn = {abc, ABC, cAcBcC, vec-cev} {
190
\int_compare:nNnTF \l_keys_choice_int = 1 {
191
\prop_gclear:N \g__jmt_lwa_selected_presets_prop
192
} {}
193
\prop_gput:NVn \g__jmt_lwa_selected_presets_prop \l_keys_choice_tl {}
194
},
195
presets .initial:n = {abc, ABC, cAcBcC},
196
tweaks .bool_set:N = \g__jmt_lwa_tweak_shortcuts_bool,
197
tweaks .initial:n = {true},
198
linewidth .fp_set:N = \g__jmt_lwa_line_width,
199
linewidth .initial:n = {.3},
200
}
201
\ProcessKeysPackageOptions{letterswitharrows}
(End definition for \g__jmt_lwa_tweak_shortcuts_bool , \g__jmt_lwa_selected_presets_prop , and
\__jmt_lwa_arrow_draw:nnn.)
\v<char>
\<char>v
\vleft
\vright
202
\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {abc} {
203
\int_step_inline:nnn {1} {26} {
204
\int_compare:nNnTF {#1} = {22} {
205
\cs_new:cpx {vright} {
206
\exp_not:N\__jmt_lwa_arrow_overset:w{v}
207
}
208
\cs_new:cpx {vleft} {
209
\exp_not:N\__jmt_lwa_arrow_overset:w*{v}
210
}
211
} {
212
\cs_new:cpx {v\int_to_alph:n{#1}} {
213
\exp_not:N\__jmt_lwa_arrow_overset:w{\int_to_alph:n{#1}}
214
}
215
\cs_new:cpx {\int_to_alph:n{#1}v} {
216
\exp_not:N\__jmt_lwa_arrow_overset:w*{\int_to_alph:n{#1}}
217
}
218
}
219
}
220
} {}
(End definition for \v<char> and others. These functions are documented on page1.)
\v<CHAR>
\<CHAR>v
221\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {ABC} {
222
\int_step_inline:nnn {1} {26} {
223
\cs_new:cpx {v\int_to_Alph:n{#1}} {
224
\exp_not:N\__jmt_lwa_arrow_overset:w{\int_to_Alph:n{#1}}
225
}
226
\cs_new:cpx {\int_to_Alph:n{#1}v} {
227
\exp_not:N\__jmt_lwa_arrow_overset:w*{\int_to_Alph:n{#1}}
228
}
229
}
230
} {}
(End definition for \v<CHAR> and \<CHAR>v. These functions are documented on page2.)
\vc<CHAR>
\c<CHAR>v
231\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {cAcBcC} {
232
\int_step_inline:nnn {1} {26} {
233
\cs_new:cpx {vc\int_to_Alph:n{#1}} {
234
\exp_not:N\__jmt_lwa_arrow_overset:w{\exp_not:N\mathcal{\int_to_Alph:n{#1}}}
235
}
236
\cs_new:cpx {c\int_to_Alph:n{#1}v} {
237
\exp_not:N\__jmt_lwa_arrow_overset:w*{\exp_not:N\mathcal{\int_to_Alph:n{#1}}}
238
}
239
}
240
} {}
(End definition for \vc<CHAR> and \c<CHAR>v. These functions are documented on page2.)
\vec
\cev
241\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {vec-cev} {
242
\RenewDocumentCommand \vec {m} {
243
\__jmt_lwa_arrow_overset:w {#1} \scan_stop:
244
}
245
\DeclareDocumentCommand \cev {m} {
246
\__jmt_lwa_arrow_overset:w* {#1} \scan_stop:
247
}
248
} {}
(End definition for \vec and \cev. These functions are documented on page2.)
Some personal-preference tweaks.
249
\bool_if:NTF \g__jmt_lwa_tweak_shortcuts_bool {
250
\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {ABC} {
251
\int_step_inline:nnn {1} {26} {
252
\cs_set:cpx {v\int_to_Alph:n{#1}} {
253
\exp_not:N\__jmt_lwa_arrow_overset:w[2.5mu][8/10]{\int_to_Alph:n{#1}}
254
}
255
\cs_set:cpx {\int_to_Alph:n{#1}v} {
256
\exp_not:N\__jmt_lwa_arrow_overset:w*[2.5mu][7/10]{\int_to_Alph:n{#1}}
257
}
258
}
259
\cs_set:cpn {vS} {
260
\__jmt_lwa_arrow_overset:w[3mu][7/10]{S}
261
}
262
\cs_set:cpn {vT} {
263
\__jmt_lwa_arrow_overset:w[2mu][8/10]{T}
264
}
265
\cs_set:cpn {Tv} {
266
\__jmt_lwa_arrow_overset:w*[1mu][8/10]{T}
267
}
268
\cs_set:cpn {vU} {
269
\__jmt_lwa_arrow_overset:w[2mu][7/10]{U}
270
}
271
\cs_set:cpn {Uv} {
272
\__jmt_lwa_arrow_overset:w*[2mu][7/10]{U}
273
}
274
\cs_set:cpn {vV} {
275
\__jmt_lwa_arrow_overset:w[2.5mu][7/10]{V}
276
}
277
\cs_set:cpn {Vv} {
278
\__jmt_lwa_arrow_overset:w*[2mu][7/10]{V}
279
}
280
\cs_set:cpn {vX} {
281
\__jmt_lwa_arrow_overset:w[3mu][7/10]{X}
282
}
283
\cs_set:cpn {vY} {
284
\__jmt_lwa_arrow_overset:w[2mu][8/10]{Y}
285
}
286
\cs_set:cpn {Yv} {
287
\__jmt_lwa_arrow_overset:w*[2mu][7/10]{Y}
288
}
289
} {}
290
\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {cAcBcC} {
291
\int_step_inline:nnn {1} {26} {
292
\cs_set:cpx {vc\int_to_Alph:n{#1}} {
293
\exp_not:N\__jmt_lwa_arrow_overset:w[1mu][9/10]{\exp_not:N\mathcal{\int_to_Alph:n{#1}}}
294
}
295
\cs_set:cpx {c\int_to_Alph:n{#1}v} {
296
\exp_not:N\__jmt_lwa_arrow_overset:w*[1.5mu][8/10]{\exp_not:N\mathcal{\int_to_Alph:n{#1}}}
297
}
298
}
299
} {}
300
\prop_if_in:NnTF \g__jmt_lwa_selected_presets_prop {abc} {
301
\cs_new:cpn {vell} {
302
\__jmt_lwa_arrow_overset:w{\ell}
303
}
304
\cs_new:cpn {ellv} {
305
\__jmt_lwa_arrow_overset:w{\ell}
306
}
307
} {}
308