Help me understand behaviour when redefining \textbullet
What’s going on here is that the definition of \textbullet
looks several things up at the point of use, so trying to save the symbol with \let
fails.
The Fix
If you’re compiling in a modern engine such as LuaLaTeX or XeLaTeX, the libertinus
package loads fontspec
, unicode-math
, and the Unicode versions of the text and math fonts.
These fonts support the Unicode symbols ⚫, •, ∙, etc. It even has unicode-math
define the macros \vysmblkcircle
, \smblkcircle
, \mdblkcircle
, etc. for them. The Libertinus fonts have some but not all of these, so if you want to redefine \textbullet
as the larger one, you want to first select a font that does contain it. I chose DejaVu Sans.
If you’re compiling in a traditional TeX engine, such as PDFLaTeX, you want to define \oldbullet
as slot "88
of the TS1 encoding (essentially duplicating the old definition of \textbullet
in textcomp
, rather than saving the more complicated command in the modern LaTeX kernel). If you’re going to be using a font that doesn’t come with a TS1 extension, you’d want to define \textbullet
to select a font family and encoding along with the font size. Since you’re using libertinus
and no other font packages, that’s not a problem here.
However, you don’t want to select \large
size all the time, even in, for example, a footnote. What you more likely want is for the symbol to be one size larger than the current font. You can accomplish this with relsize
.
A sample:
\tracinglostchars=2 % Print a warning to the console if a character is missing.
\documentclass[11pt]{report}
\usepackage{iftex}
\usepackage{libertinus}
\ifTUTeX
% The libertinus package loaded fontspec and unicode-math, with Libertinus
% Math as the math font.
\newfontfamily\symbolfont{DejaVu Sans}[Scale=MatchLowercase]
\DeclareTextSymbol{\oldbullet}{\UnicodeEncodingName}{"2022}
\DeclareTextSymbolDefault{\oldbullet}{\UnicodeEncodingName}
\renewcommand\textbullet{{\symbolfont\symbol{"26AB}}}
\else
% The libertinus package loaded the legacy Type 1 font.
\usepackage[T1]{fontenc}
\usepackage[libertine]{newtxmath}
\usepackage{relsize}
\DeclareTextSymbol{\oldbullet}{TS1}{"88}
\DeclareTextSymbolDefault{\oldbullet}{TS1}
\renewcommand\textbullet{{\larger\oldbullet}}
\fi
\begin{document}
\oldbullet\textbullet$\mathord\bullet$
\end{document}
If you also want U+2022 to be equivalent to \textbullet
in text mode, and \bullet
in math mode, you would add something like:
\usepackage{newunicodechar}
\newunicodechar{^^^^2022}{\ifmmode\bullet\else\textbullet\fi}
What’s Going on?
The LaTeX kernel declares \textbullet
in one of three different encodings: the legacy symbol from the TS1 8-bit encoding, the even-older legacy symbol from the OMS 7-bit encoding, and the Unicode symbol. This means the command for \textbullet
doesn’t know ahead of time which font or encoding it will be using, and trying to save the definition with \let
and then redefine \textbullet
won’t work as expected.
The historical reason for this was that the bullet operator was first defined as a math symbol in the ’80s. In 1996, Jörg Knappen defined a text-symbol encoding, TS1, as a companion for the new T1 Cork encoding. However, the majority of fonts available at the time supported less than half of TS1. As a bug workaround, for the next twenty years, LaTeX authors would load the textcomp
package, which would attempt to detect the symbols the current font supported and fall back to poor-man’s versions if necessary.
There is no longer any need to load a separate package: this code is now in the LaTeX kernel.
The definition is documented in §20.4.1 of the LaTeX2e sources. The relevant part is that \DeclareTextCommand
expands to code that checks the current encoding and whether we are in math mode. This refers to the \?\textbullet
defined by \DeclareTextCommandDefault
, which eventually expands in a recursive way.
This happens because you just define the top-level \oldbullet
, and it expands to \?-cmd \textbullet \?\textbullet
, and that eventually expands to your redefined \textbullet
which is \large\oldbullet
.
As mentioned in the comment, this is basically the same issue as with robust commands: they are not one single macro, but a set of macros under similar name as the top-level macro, and when you redefine one, you have to redefine them all, otherwise you get an unwanted behaviour (this, however, isn't covered by letltxmacro
).
To do what you want you can, instead of copying the definition of \textbullet
to a new name, you can declare \oldbullet
the same way as \textbullet
is declared by LaTeX:
\DeclareTextSymbol{\oldbullet}{TS1}{136}
\DeclareTextSymbolDefault{\oldbullet}{TS1}
The first line means that the command \oldbullet
will take the character number 136
from the current font in encoding TS1
, and the second line says that the default encoding for \oldbullet
is TS1
, so LaTeX changes the encoding if necessary to use that symbol (otherwise the symbol would always use the current encoding, no matter which it is).
Then you can redefine \textbullet
normally:
\documentclass[11pt]{report}
\usepackage{libertinus}
% Resize \textbullet to make it a bit larger (closer to Computer Modern)
\DeclareTextSymbol{\oldbullet}{TS1}{136}
\DeclareTextSymbolDefault{\oldbullet}{TS1}
\renewcommand*\textbullet{{\LARGE\oldbullet}}
\begin{document}
\oldbullet\textbullet$\mathord\bullet$
\indent\kern1pt\raise5pt\hbox{$\smile$}
\end{document}