4

This is a followup to Using soul with indirect formatting applied, where it was determined that using \soulregister allows the soul macros to have font switching macros within the parameters (specifically \ul in this case). However, I am not able to get this to work if the macro has more than one parameter.

Single Parameter: \ApplyStyleA

This seems to work fine if the macro has one mandatory parameter, as per the answer in Using soul with indirect formatting applied.

Two Mandatory Parameters: \ApplyStyleB

However, if the macro has two mandatory parameters, then I get:

Extra }, or forgotten \endgroup

Two Parameters (one optional): \ApplyStyleC

Or, for the case where there is one optional parameter and one mandatory parameter, I get:

TeX capacity exceeded, sorry [input stack size=5000]

Notes:

  • The MWE as is below compiles fine as the two problematic lines are commented out. So, one needs to uncomment one of those to reproduce the problem.

Code:

\documentclass{article}
\usepackage{xparse}
\usepackage{xcolor}
\usepackage{soul}
\usepackage{xspace}


%% ----------------------------------------------------------------
%%                https://tex.stackexchange.com/questions/36894/
%%                    underline-omitting-the-descenders
%%
%%
\makeatletter
\ExplSyntaxOn
    \cs_new:Npn \white_text:n #1
      {
        \fp_set:Nn \l_tmpa_fp {.01}
        \fp_mul:Nn \l_tmpa_fp {#1}
        \llap{\textcolor{white}{\the\SOUL@syllable}\hspace{\fp_to_decimal:N \l_tmpa_fp em}}
        \llap{\textcolor{white}{\the\SOUL@syllable}\hspace{-\fp_to_decimal:N \l_tmpa_fp em}}
      }
    \NewDocumentCommand{\whiten}{ m }
        {
          \int_step_function:nnnN {1}{1}{#1} \white_text:n
        }
\ExplSyntaxOff

\NewDocumentCommand{ \varul }{ D<>{5} O{0.2ex} O{0.1ex} +m } {%
\begingroup
\setul{#2}{#3}%
\def\SOUL@uleverysyllable{%
   \setbox0=\hbox{\the\SOUL@syllable}%
   \ifdim\dp0>\z@
      \SOUL@ulunderline{\phantom{\the\SOUL@syllable}}%
      \whiten{#1}%
      \llap{%
        \the\SOUL@syllable
        \SOUL@setkern\SOUL@charkern
      }%
   \else
       \SOUL@ulunderline{%
         \the\SOUL@syllable
         \SOUL@setkern\SOUL@charkern
       }%
   \fi}%
    \ul{#4}%
\endgroup
}%
\makeatother
%% ----------------------------------------------------------------


\newcommand{\ApplyStyleA}[1]{\textit{#1}}
\soulregister\ApplyStyleA{1}

\newcommand{\ApplyStyleB}[2]{\textit{#1}}
\soulregister\ApplyStyleB{2}

\newcommand{\ApplyStyleC}[2][]{\textit{#2}}
\soulregister\ApplyStyleC{2}

\begin{document}
This one works just fine:
\varul{\ApplyStyleA{special text}\xspace}%

\medskip
These one leads to ``Extra \}, or forgotten \verb|\endgroup|."
%\varul{\ApplyStyleB{special text}{second paramater}\xspace}%

\medskip
This one leads to ``TeX capacity exceeded, sorry [input stack size=5000]"
%\varul{\ApplyStyleC[first optional paramater]{special text}\xspace}%
\end{document}
  • The 0 and 1 aremore an identifier and not specifically the number of arguments. The digits 7, 8 and 9 are reserved for superscripts, footnotes and accents respectively. The number is used in \SOUL@docmd in a few conditionals. I fear that your commands won’t be recognized by soul out of the box. It is also important to know what the optional argument does. Will the content be typeset? How does the command work? – Qrrbrbirlbel Jul 24 '13 at 22:16
6

The digit that is used in \soulregister does not specify the number of arguments per se.

It is used as an identifier. The digits 7, 8 and 9 are used for \textsuperscript (“and similar”), \footnote and accents respectively.

Depending on what the two arguments in \ApplyStyleB do (will they both be content that shall be typeset or is one of them used for another purpose) there see various solutions possible.

The same holds true for the \ApplyStyleC macro though this is harder to fix as the default value for #1 is hidden.

Please notice the difference between a simple expansion \ApplyStyleBa (type 3) which will apply the underlining to everything to what \ApplyStyleBa expands. The \ApplyStyleBb macro uses the proper way as a 1 macro and applies the souling only to its arguments (type 2).

Type 4 includes one optional argument. If you want soul’s effects also to affect the default optional argument (in your case an empty string, in my case I'm the default!), said default text is to be extracted and used in the redefinition of \SOUL@@.

Though, if you use the optional argument not to typeset it but to use somewhere else, there is a different approach needed. A few macros (\minipage or \parbox) uses their first mandatory argument for a dimension, the optional argument as an alignment. To both of these one shouldn’t apply underlining.

Code

\documentclass{article}
\usepackage{xparse}
\usepackage{xcolor}
\usepackage{soul}
\usepackage{xspace}


%% ----------------------------------------------------------------
%%                http://tex.stackexchange.com/questions/36894/
%%                    underline-omitting-the-descenders
%%
%%
\makeatletter
\ExplSyntaxOn
    \cs_new:Npn \white_text:n #1
      {
        \fp_set:Nn \l_tmpa_fp {.01}
        \fp_mul:Nn \l_tmpa_fp {#1}
        \llap{\textcolor{white}{\the\SOUL@syllable}\hspace{\fp_to_decimal:N \l_tmpa_fp em}}
        \llap{\textcolor{white}{\the\SOUL@syllable}\hspace{-\fp_to_decimal:N \l_tmpa_fp em}}
      }
    \NewDocumentCommand{\whiten}{ m }
        {
          \int_step_function:nnnN {1}{1}{#1} \white_text:n
        }
\ExplSyntaxOff

\NewDocumentCommand{ \varul }{ D<>{5} O{0.2ex} O{0.1ex} +m } {%
\begingroup
\setul{#2}{#3}%
\def\SOUL@uleverysyllable{%
   \setbox0=\hbox{\the\SOUL@syllable}%
   \ifdim\dp0>\z@
      \SOUL@ulunderline{\phantom{\the\SOUL@syllable}}%
      \whiten{#1}%
      \llap{%
        \the\SOUL@syllable
        \SOUL@setkern\SOUL@charkern
      }%
   \else
       \SOUL@ulunderline{%
         \the\SOUL@syllable
         \SOUL@setkern\SOUL@charkern
       }%
   \fi}%
    \ul{#4}%
\endgroup
}%

\def\SOUL@docmd#1#2{%
    \ifx9#1%
        \def\SOUL@@{\SOUL@addgroup{#2}}%
    \else\ifx8#1%
        \SOUL@doword
        \def\SOUL@@##1{%
            \SOUL@token={\footnotemark}%
            \SOUL@everytoken
            \SOUL@syllable={\footnotemark}%
            \SOUL@everysyllable
            \footnotetext{##1}%
            \SOUL@doword
            \SOUL@scan
        }%
    \else\ifx7#1%
        \SOUL@doword
        \def\SOUL@@##1{%
            \SOUL@token={#2{##1}}%
            \SOUL@everytoken
            \SOUL@syllable={#2{##1}}%
            \SOUL@everysyllable
            \SOUL@doword
            \SOUL@scan
        }%
    \else\ifx1#1%
        \SOUL@doword
        \def\SOUL@@##1{%
            #2{\protect\SOUL@do{##1}}%
            \SOUL@scan
        }%
    \else\ifx2#1%
        \SOUL@doword
        \def\SOUL@@##1##2{%
            #2{\protect\SOUL@do{##1}}{\protect\SOUL@do{##2}}%
            \SOUL@scan
        }%
    \else\ifx3#1%
        \SOUL@doword
        \def\SOUL@@{\expandafter\SOUL@scan#2}%
    \else\ifx4#1
        \SOUL@doword
        \def\@tempa{\renewcommand\SOUL@@[2][}%
        \expandafter\expandafter\expandafter\@tempa\expandafter\SOUL@get@optional@argument#2]{#2[\protect\SOUL@do{##1}]{\protect\SOUL@do{##2}}}%
    \else
        \SOUL@doword
        #2%
        \let\SOUL@@\SOUL@scan
    \fi\fi\fi\fi\fi\fi\fi
    \SOUL@@
}
\def\SOUL@get@optional@argument#1#2#3#4{#4}%
\makeatother
%% ----------------------------------------------------------------


\newcommand{\ApplyStyleA}[1]{\textit{#1}}
\soulregister\ApplyStyleA{1}

\newcommand{\ApplyStyleBa}[2]{\textit{#1} affected? (#2)}
\soulregister\ApplyStyleBa{2}
\newcommand{\ApplyStyleBb}[2]{\textit{#1} affected? (#2)}
\soulregister\ApplyStyleBb{3}

\newcommand{\ApplyStyleC}[2][I'm the default!]{\textit{#2} (I'm not affected again) [#1]}
\soulregister\ApplyStyleC{4}

\begin{document}
\varul{\ApplyStyleA{special text}\xspace}%

\medskip
\varul{\ApplyStyleBa{special text}{second paramater}\xspace}\par %
\varul{\ApplyStyleBb{special text}{second paramater}\xspace}% only expansion

\medskip
\varul{\ApplyStyleC[first optional paramater]{special text}\xspace}\par
\varul{\ApplyStyleC{special text}\xspace}%
\end{document}

Output

enter image description here

  • I am getting \end occurred when \ifx on line 125 was incomplete messages. The 2nd parameter is used to control the formatting options. I need the entire output of the macro underlined -- Perhaps that is already built into your solution, but since I didn't see this until now (looks like notifications are broken), I may not have enough time to play around with this until tomorrow. – Peter Grill Jul 25 '13 at 4:43
  • @PeterGrill I have updated my post. It seems like I somehow missed a \fi. Though I'm currently on the phone and can't test it. I'm not a soul user, maybe you can include a definition of the macros in your question that covers the real use of them? – Qrrbrbirlbel Jul 25 '13 at 5:15

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.