13

I want to display titles using a spelling routine I have found in this interesting discussion.

The idea is that different simple effects can be easily selected. Unfortunately the code below has some limitations that I have not been able to overcome: It does not accept blank spaces, accents or macros.

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{lcg}

% decoration
\def\efect{}
\def\decorate#1#2{\reinitrand[first=-#1,last=#1]\aux#2\end}
\def\aux#1{%
    \ifx\end#1
    \else
        \efect#1%
        \expandafter\aux
    \fi}

% efects
\newcommand{\jumpingbox}[1]{\rand\raisebox{\therand pt}{\fbox{#1}}}
\newcommand{\rotationbox}[1]{\rand\rotatebox{\therand}{\fbox{#1}}}

\begin{document}
\noindent
\let\efect\jumpingbox
\decorate{2}{The quick brown fox jumps}\\
\let\efect\rotationbox
\decorate{10}{The quick brown fox jumps}\\
\decorate{10}{{{The}}{{quick}}{{brown}}{{fox}}{{jumps}}{{\dots}}}
\end{document}

enter image description here

Notes:

  1. None of the other solutions in the link above support accents (nor utf-8 input encoding, I suppose). Most of them do not support blank spaces either.
  2. Somehow, I also would have the "word by word" feature.
  3. It would be pedagogical for me working over the TeX code. It looks like a simple recursive routine. However, I suspect that it may hide a very dark world within.
  4. My attempts:

Adding a conditional sentence for whitespaces

\if#1 %
    { }
\else
    \efect#1%
\fi

and an a horrible syntax to call it

\decorate{2}{The{ }quick{ }brown{ }fox{ }jumps{ 
over{ }Mar{{í}}a}\\ % <- note the accentuated 'i'

they can partially solve the problems, but obviously it can't be taken as a definite solution.

2 Answers 2

13

You can make the argument of \aux delimited by a space, so it operates word-by-word, rather than token-by-token (which is why it doesn't work with accented characters: they are made of multiple tokens).

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{lcg}

% decoration
\def\effect{}
\def\decorateEND{\decorateEND}
\edef\decorate#1#2{%
  \noexpand\reinitrand[first=-#1,last=#1]%
  \noexpand\decorateAUX#2 %
  \noexpand\decorateEND\space}
\def\decorateAUX#1 {%
  \ifx\decorateEND#1%
  \else
    \effect{#1}%
    \expandafter\decorateAUX
  \fi}

% effects
\newcommand{\jumpingbox}[1]{\rand\raisebox{\therand pt}{\fbox{#1}}}
\newcommand{\rotationbox}[1]{\rand\rotatebox{\therand}{\fbox{#1}}}

\begin{document}
\noindent
\let\effect\jumpingbox
\decorate{2}{The quick brown fox jumps}\\
\let\effect\rotationbox
\decorate{10}{The quick brown fox jumps}\\
\decorate{10}{The quick brown fox jumps \dots}\\
\decorate{10}{The quick brown fox jumps over María}
\end{document}

enter image description here


expl3 provides handy tools for mapping through lists and getting random numbers. You can split the input in the spaces using \seq_set_split:Nnn, and then loop through that list using \seq_map_inline:Nn, applying \effect. Then use \int_rand:nn {min} {max} to generate a random number in that interval. The code is pretty straightforward:

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{xparse}
\newcommand\effect{}
\ExplSyntaxOn
\NewDocumentCommand \decorate { m m }
  { \emoro_decorate:nn {#1} {#2} }
\seq_new:N \l__emoro_words_seq
\cs_new_protected:Npn \emoro_decorate:nn #1 #2
  {
    \seq_set_split:Nnn \l__emoro_words_seq { ~ } {#2}
    \seq_map_inline:Nn \l__emoro_words_seq
      { \effect { \int_rand:nn {-#1} {#1} } {##1} }
  }
\ExplSyntaxOff

% efects
\newcommand{\jumpingbox}[2]{\raisebox{#1pt}{\fbox{#2}}}
\newcommand{\rotationbox}[2]{\rotatebox{#1}{\fbox{#2}}}

\begin{document}
\noindent
\let\effect\jumpingbox
\decorate{2}{The quick brown fox jumps}\\
\let\effect\rotationbox
\decorate{10}{The quick brown fox jumps}\\
\decorate{10}{The quick brown fox jumps \dots}\\
\decorate{10}{The quick brown fox jumps over María}
\end{document}

To work letter-by-letter, rather than token-by-token you need a bit of parsing. In XeTeX or LuaTeX that is trivial, since the engine itself is Unicode-aware, i and í and ι are all single tokens, so you just have to detect spaces. In pdfTeX, the accented letter í is composed of two tokens which map to the UTF8 codepoint for í.

A “special” character, for example í, will expand to something like \UTFviii@two@octets <byte> or \UTFviii@three@octets <byte> or \UTFviii@four@octets <byte>, which tells us how many bytes the letter we're looking at is made of. Once we know that, we can just grab that amount of tokens and pass them together to \effect.

In the code below this is done in \__emoro_decorate_token:N. The \tl_case:NnTF test looks at the expansion of the current token, and if it starts with either \UTFviii@<some>@octets then it calls the appropriate macro to grab what remains of the character and pass it to \effect. The rest of the code is just to loop through the argument token list and separate single-characters, spaces, and grouped tokens (you can find a brief description of this loop mechanism here and here). You can use another looping mechanism of your liking.

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{xparse}
\newcommand\effect{}
\makeatletter
\ExplSyntaxOn
\NewDocumentCommand \decoratewords { m m }
  { \emoro_decorate_words:nn {#1} {#2} }
\seq_new:N \l__emoro_words_seq
\cs_new_protected:Npn \emoro_decorate_words:nn #1 #2
  {
    \seq_set_split:Nnn \l__emoro_words_seq { ~ } {#2}
    \seq_map_inline:Nn \l__emoro_words_seq
      { \effect {#1} {##1} }
  }
%
\NewDocumentCommand \decorateletters { m m }
  { \emoro_decorate_letters:nn {#1} {#2} }
\tl_new:N \l__emoro_parm_tl
\tl_new:N \l__emoro_output_tl
\cs_new_protected:Npn \emoro_decorate_letters:nn #1 #2
  {
    \tl_set:Nn \l__emoro_parm_tl {#1}
    \tl_clear:N \l__emoro_output_tl
    \__emoro_decorate_loop:w #2
      \q_recursion_tail \q_recursion_stop
  }
\cs_new_protected:Npn \__emoro_decorate_loop:w #1 \q_recursion_stop
  {
    \tl_if_head_is_N_type:nTF {#1}
      { \__emoro_decorate_token:N }
      {
        \tl_if_head_is_group:nTF {#1}
          { \__emoro_decorate_group:n }
          { \__emoro_decorate_space:w }
      }
    #1 \q_recursion_stop
  }
\cs_new_protected:Npn \__emoro_decorate_token:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1
      { \tl_use:N \l__emoro_output_tl }
    \exp_args:NNo \exp_args:No \tl_case:NnTF
        { \exp_after:wN \tl_head:w #1 \q_stop }
      {
        { \UTFviii@two@octets   } { \__emoro_UTFviii_two:NNw    }
        { \UTFviii@three@octets } { \__emoro_UTFviii_three:NNNw }
        { \UTFviii@four@octets  } { \__emoro_UTFviii_four:NNNNw }
      }
        {#1}
        { \__emoro_add_output:nw { \__emoro_effect:n {#1} } }
  }
\cs_new_protected:Npn \__emoro_UTFviii_two:NNw #1 #2
  { \__emoro_add_output:nw { \__emoro_effect:n {#1#2} } }
\cs_new_protected:Npn \__emoro_UTFviii_three:NNNw #1 #2 #3
  { \__emoro_add_output:nw { \__emoro_effect:n {#1#2#3} } }
\cs_new_protected:Npn \__emoro_UTFviii_four:NNNNw #1 #2 #3 #4
  { \__emoro_add_output:nw { \__emoro_effect:n {#1#2#3#4} } }
\cs_new_protected:Npn \__emoro_decorate_group:n #1
  { \__emoro_add_output:nw { \__emoro_effect:n {#1} } }
\exp_last_unbraced:NNo
\cs_new_protected:Npn \__emoro_decorate_space:w { \c_space_tl }
  { \__emoro_add_output:nw { \__emoro_effect:n { ~ } } }
\cs_new_protected:Npn \__emoro_add_output:nw #1 #2 \q_recursion_stop
  {
    \tl_put_right:Nn \l__emoro_output_tl {#1}
    \__emoro_decorate_loop:w #2 \q_recursion_stop
  }
\cs_new_protected:Npn \__emoro_effect:n #1
  { \exp_args:NV \effect \l__emoro_parm_tl {#1} }
\cs_new_eq:NN \intrand \int_rand:nn
%
\ExplSyntaxOff

% efects
\newcommand{\jumpingbox}[2]{%
  \if\space#2%
    \space
  \else
    \raisebox{\intrand{-#1}{#1}pt}{\fbox{#2}}%
  \fi}
\newcommand{\rotationbox}[2]{%
  \if\space#2%
    \space
  \else
    \rotatebox{\intrand{-#1}{#1}}{\fbox{#2}}%
  \fi}

\begin{document}

\noindent
\let\effect\jumpingbox
\decoratewords{2}{The quick brown fox jumps}\\
\let\effect\rotationbox
\decoratewords{10}{The quick brown fox jumps}\\
\decoratewords{10}{The quick brown fox jumps \dots}\\
\decoratewords{10}{The quick brown fox jumps over María}

\noindent
\let\effect\jumpingbox
\decorateletters{2}{The quick brown fox jumps}\\
\let\effect\rotationbox
\decorateletters{10}{The quick brown fox jumps}\\
\decorateletters{10}{The quick brown fox jumps \dots}\\
\decorateletters{10}{The quick brown fox jumps over María}
\end{document}

enter image description here

5
  • Ok, I understand it more or less. It resolves immediately the word-by-word feature, but not the letter-by-letter one, which is the main goal...
    – e_moro
    May 23, 2020 at 1:26
  • 1
    @e_moro I added a version that copes with Unicode characters May 23, 2020 at 3:23
  • In how many tokens is a char like á?
    – manooooh
    May 23, 2020 at 12:55
  • 1
    @manooooh á is made of two tokens (because it's made of two bytes in utf8 encoding). You can see that by typing \show á in a document; the terminal will show you > �=macro:->\UTFviii@two@octets �, which means it's composed of two octets/tokens/bytes. Except for ASCII, which is one single byte, most of the characters you type (languages based on the Latin alphabet) are two utf8 bytes (see this table: en.wikipedia.org/wiki/UTF-8#Codepage_layout). Mostly CJK and some Asian scripts are composed of three bytes, and special-purpose characters are 4-bytes long. May 23, 2020 at 13:24
  • 1
    @manooooh á is one token in nowadays used TeX engines like LuaTeX or XeTeX.
    – wipet
    May 26, 2020 at 19:59
7

Here is a solution based on this answer.

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{lcg}
\usepackage{soul}

\makeatletter
\def\SOUL@soeverytoken{%
 \efect{\the\SOUL@token}}
\makeatother

\newcommand{\jumpingbox}[1]{\rand\raisebox{\therand pt}{\fbox{#1}}}
\newcommand{\rotationbox}[1]{\rand\rotatebox{\therand}{\fbox{#1}}}

\def\decorate#1#2{\reinitrand[first=-#1,last=#1]\so{#2}}

\begin{document}
\noindent
\let\efect\jumpingbox
\decorate{2}{The quick brown fox jumps}\\
\let\efect\rotationbox
\decorate{10}{The quick brown fox jumps}\\
\decorate{10}{{{The}}{{quick}}{{brown}}{{fox}}{{jumps}}{{\dots}}}
\end{document}

enter image description here

BTW, for us cats foxes aren't that fast.

2
  • Yess, probably the simplest solution in the "classic" thread we mentioned. However, It doesn't support accents, unless you uses the awful syntax "Mar{{í}}a"
    – e_moro
    May 23, 2020 at 2:04
  • 1
    @e_moro I never use these. However, things like \decorate{10}{The quick brown f\"ox jumps} works perfectly. I am not using all these special characters because overall my experience is not satisfactory with them. I know I am a minority, but what you are saying is that soul has problems with these, I am happy to believe this but IMHO soul is a standard package so, if anything, then packages like this one will provide us with the most stable solutions.
    – user194703
    May 23, 2020 at 2:35

You must log in to answer this question.

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