10

I am trying to implement the LaTeX command \text{} which makes texts in math mode behave as if outside math mode.

My current attempt is as follows.

\def\text#1{\begingroup\rm\testing#1\endtest}
{\escapechar=-1\xdef\endtest{\string\\endtest}}
\def\testing#1#2{\ifx#2\endtest\let\next=\endgroup\else\let\next=\testing\fi%
{\catcode`\|=10\ifcat#1|\hskip.5em\else#1\fi}\next #2}

This has at least 2 drawbacks: First, it produces a \endtest after the text; second, there are no spaces in the texts.

For example,

\text{example haha}

gives

examplehaha\endtest.

I am running out of ideas to get rid of the \endtest and produce a space in the texts.


Any help or reference is highly appreciated by a newbie like me. Thanks in advance.

  • Can't you copy the definition of \text? I think it's some sort of \hbox inside \mathchoice. – Manuel Oct 5 '16 at 16:27
  • For reference, the definition is found in amstext.sty. (kpsewhich amstext.sty will tell you where it is.) – Torbjørn T. Oct 5 '16 at 16:29
  • 1
    There is \hbox{...} for text, which can be used inside math. And \mathchoice helps, when you want to have different font sizes depending on the current math style. – Heiko Oberdiek Oct 5 '16 at 16:36
  • @Manuel Thanks, but I don't see how I am going to implement that implementation, without LaTeX. I don't understand how it works either... – awllower Oct 5 '16 at 16:37
  • @HeikoOberdiek Thanks for informing me of this use of \hbox and the interesting control sequence \mathchoice. :) – awllower Oct 5 '16 at 16:42
15

Really in plain you just have to use

\hbox{hello world}

in amsmath (or more exactly amstext) \text is defined to use \mathchoice and use different size text is subscripts etc, however plain tex has no real font size changing commands so there is, by default, nothing comparable you can do. If you restrict to \rm then that font is set up at different sizes in \fam0 so you could do

\def\textrm#1{\mathchoice
{\hbox{\the\textfont0 #1}}
{\hbox{\the\textfont0 #1}}
{\hbox{\the\scriptfont0 #1}}
{\hbox{\the\scriptscriptfont0 #1}}}


$$\textrm{one two} x^{\textrm{one two}^{\textrm{one two}}}$$

\bye

enter image description here

  • Thanks for daunting me on the use of \hbox. It really saved my day. :) – awllower Oct 6 '16 at 3:56
15

You can use opmac (which I recommend anyway if you insist in using plain TeX):

\input opmac

\def\text#1{%
  \relax
  \ifmmode
    \mathchoice
      {\hbox{#1}}
      {\hbox{#1}}
      {\hbox{\typoscale[700/]\relax#1}}
      {\hbox{\typoscale[500/]\relax#1}}%
  \else
    \hbox{#1}%
  \fi
}
$$
a\text{a}_{a\text{a}_{a\text{a}}}
$$
$$
a+b=b+a\text{ is a {\it nice\/} property}
$$
$$
\scriptstyle a+b=b+a\text{ is a {\it nice\/} property}
$$
$$
\scriptscriptstyle a+b=b+a\text{ is a {\it nice\/} property}
$$

\bye

enter image description here


Just for the fun of it, let me try describing what your macros do.

\def\text#1{\begingroup\rm\testing#1\endtest}
{\escapechar=-1\xdef\endtest{\string\\endtest}}
\def\testing#1#2{\ifx#2\endtest\let\next=\endgroup\else\let\next=\testing\fi%
{\catcode`\|=10\ifcat#1|\hskip.5em\else#1\fi}\next #2}

First \text is defined to absorb its argument and return the token list

\begingroup\rm\testing<argument>\endtesting

The macro \testing is defined to have two arguments. If the second argument is \endtest, the control sequence \next is defined to be \endgroup else it is defined to be \testing, which starts up a recursion. Then

{\catcode`\|=10\ifcat#1|\hskip.5em\else#1\fi}

is done, where #1 stands for the first token (or contents of braced group) following \testing. The test \ifcat#1| will not succeed if #1 is a space for two reasons: spaces are ignored when TeX is looking for an undelimited argument, so #1 will never be a space token; second reason, the test will succeed when #1 is a single token of category code 12, which is the category code of | at definition's time.

Thus you get a space when a character such as . (catcode 12) follows (and the character is ignored), or the character otherwise, if it is a letter. If #1 is the contents of a braced group, then, well, anything can happen. Finally \next is executed, and #2 is reinserted. This is the reason for “endtest” to appear at the end.

Recall that when an argument is absorbed, category codes are frozen, so the setting \catcode`\|=10 does nothing at all.

A recursion without the “termination” problem is obtained by adding two terminators.

% generic terminators
\def\TerminatorA{\TerminatorA}
\def\TerminatorB{\TerminatorB}

\def\boldenas#1{\boldenasRecurse#1\TerminatorA\TerminatorB}
\def\boldenasRecurse#1#2#3\TerminatorB{%
  \boldenasDo{#1}%
  \ifx#2\TerminatorA
    \let\next\boldenasEnd
  \else
    \let\next\boldenasRecurse
  \fi
  \next#2#3\TerminatorB
}
\def\boldenasDo#1{%
  \ifx #1a%
    {\bf a}%
  \else
    #1%
  \fi
}
\def\boldenasEnd#1\TerminatorB{}


\boldenas{abracadabra}

\bye

This is not safe against #1 being empty (we're tough plain TeX users, after all). Spaces will be gobbled for the reason I explained earlier and braced groups will produce chaos. But, hey, we're doing theory!

Note that the \let instructions can be avoided:

% generic terminators
\def\TerminatorA{\TerminatorA}
\def\TerminatorB{\TerminatorB}
% syntactic sugar
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

\def\boldenas#1{\boldenasRecurse#1\TerminatorA\TerminatorB}
\def\boldenasRecurse#1#2#3\TerminatorB{%
  \boldenasDo{#1}%
  \ifx#2\TerminatorA
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  \boldenasEnd\boldenasRecurse#2#3\TerminatorB
}
\def\boldenasDo#1{%
  \ifx #1a%
    {\bf a}%
  \else
    #1%
  \fi
}
\def\boldenasEnd#1\TerminatorB{}


\boldenas{abracadabra}

\bye
  • The leading space is trimmed in the script styles. Is this intended? – Guido Oct 5 '16 at 18:20
  • why is there no space between $a$ and "is" in either of the "script" styles? – barbara beeton Oct 5 '16 at 18:21
  • @barbarabeeton That's a good question! But it should be addressed to Petr. ;-) Fixed. – egreg Oct 5 '16 at 19:38
  • @Guido Apparently, \typosize is looking ahead and so it gobbles the space. Fixed. – egreg Oct 5 '16 at 19:41
  • @egreg Thanks for the explanation: I am wondering if there is a technique to get rid of that \endtest string while still able to have recursion: maybe I shall \let\next= some other macro to absorb that ending sequence? Also, it seems I have no way to absorb spaces in the argument of a macro? Finally, thanks a lot for the recommendation of opmac, it is quite a nice thing to know. :-D – awllower Oct 6 '16 at 2:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.