0

Trying to find a solution for How to set kerning in a combination of macros with a fixed character in between, I came across a related problem: when used as a superscript (in a footnote marker), certain characters will be too close to the main character, e.g. in combinations like o and superscript j.

To tackle both problems, I wanted to create a function that returns a kerning value depending on the first character of the marker’s “string value”.

My first attempts were to simply use \tl_head:n, then I read Convert string to token list? – both give the same, yet not the required result.

\documentclass{book}

\usepackage{refcount}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{expl3}

\ExplSyntaxOn
\makeatletter
\DeclareDocumentCommand \aalph {m}
 {
  \IfInteger { #1 }
   {
    \myAlph:n { #1 }
   }{
    \@ifundefined { r@#1 }
     { yyyy }
     {
      \IfInteger { \getrefnumber { #1 } }
       { \myAlph:n { \getrefnumber { #1 } } }
       { zzzz }
     }
   }
 }

\cs_new_protected:Npn \myAlph:n #1
 {
  \int_to_alph:n { #1 }
 }

\DeclareDocumentCommand \alphKern {m}
 {
  \l_alphKern:n { #1 }
 }

\cs_new:Npn \l_alphKern:n #1
 {
  \str_clear_new:N \l_alph_str
  \tl_clear_new:N \l_tmp_tl

  \str_set:Nn \l_alph_str { \aalph { #1 } }

  \tl_set_rescan:Nno \l_tmp_tl { } { \l_alph_str }

  \tl_head:n \l_tmp_tl
 }
\ExplSyntaxOff

\newcounter{test}

\begin{document}

\setcounter{test}{1}
>\aalph{\thetest}<>\alphKern{\thetest}< (exp.: >a<>a<)\\

>\aalph{27}<>\alphKern{27}< (exp.: >aa<>a<)\\

>\aalph{53}<>\alphKern{53}< (exp.: >ba<>b<)\\

>\aalph{283}<>\alphKern{283}< (exp.: >jw<>j<)\\

\end{document}

The result:

>a<>a< (exp.: >a<>a<)
>aa<>aa< (exp.: >aa<>a<)
>ba<>ba< (exp.: >ba<>b<)
>jw<>jw< (exp.: >jw<>j<)

I might be overlooking sth. really obvious – any help is appreciated!

(PS: for this one, I am using XeTeX and as the project is nearly done, I cannot change to anything else)

2
  • 1
    In your code, when #1 is \thetest, \str_set:Nn \l_alph_str { \aalph { #1 } } defines \l_alph_str as string \aalph {\thetest }, not the "return value" of \aalph{\thetest}. For fully expandable macros, you can use latex3 e-type expansion to get its expansion result. But unfortunately, the xstring macro \IfInteger is not fully expandable, hence \aalph is not fully expandable. Jun 10, 2020 at 6:09
  • @muzimuzhiZ Thank you for that very valuable hint! I can avoid \IfInteger by other means. Could you make an answer so I can accept?
    – dariok
    Jun 10, 2020 at 13:23

1 Answer 1

0

(continued from question comments)

The key point is that in \str_set:Nn \l_alph_str { \aalph { #1 } } you need to ensure \aalph { #1 } is fully expandable, then use \str_set:Ne to convert the expansion result to string and store it in \l_alph_str.

Assume you can avoid \IfInteger by other (fully expandable) means, the following example shows an attempt:

\documentclass{book}

\usepackage{refcount}
\usepackage{xstring}
\usepackage{xparse}

\ExplSyntaxOn
\makeatletter
% make \aalph itself expandable
\DeclareExpandableDocumentCommand \aalph {m}
 {
%  \IfInteger { #1 }
%   {
    \myAlph:n { #1 }
%   }{
%    \@ifundefined { r@#1 }
%     { yyyy }
%     {
%      \IfInteger { \getrefnumber { #1 } }
%       { \myAlph:n { \getrefnumber { #1 } } }
%       { zzzz }
%     }
%   }
 }

% do not use protected version
\cs_new:Npn \myAlph:n #1
 {
  \int_to_alph:n { #1 }
 }

\DeclareDocumentCommand \alphKern {m}
 {
  \l_alphKern:n { #1 }
 }

\cs_new:Npn \l_alphKern:n #1
 {
  \str_clear_new:N \l_alph_str
  \tl_clear_new:N \l_tmp_tl

  % use \str_set:Ne to fully expand its 2nd arg before converting to string
  \str_set:Ne \l_alph_str { \aalph { #1 } }

  \tl_set_rescan:Nno \l_tmp_tl { } { \l_alph_str }

  % here you need arg-spec "N", not "n"
  \tl_head:N \l_tmp_tl
 }

% this defines \str_set:Ne
\cs_generate_variant:Nn \str_set:Nn {Ne}
\ExplSyntaxOff

\newcounter{test}

\begin{document}

\setcounter{test}{1}
>\aalph{\thetest}<>\alphKern{\thetest}< (exp.: >a<>a<)\\

>\aalph{27}<>\alphKern{27}< (exp.: >aa<>a<)\\

>\aalph{53}<>\alphKern{53}< (exp.: >ba<>b<)\\

>\aalph{283}<>\alphKern{283}< (exp.: >jw<>j<)\\

\end{document}

Notes:

  • xparse already loads expl3 so I delete the \usepacakge{expl3}. Also, since LaTeX2e 2020/02/02, (a significant portion of) expl3 is pre-loaded in LaTeX2e format.
  • Fully expandable LaTeX3 functions are indicated by a star (⭑) in documentation interface3, as documented in the same doc, sec. I.2. But most other packages do not have a per-macro documentation about expandability, hence you need to check by yourself.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .