2

I'm aware of \makebox[*length*][s]{t e x t}, but I have to put spaces between each character. If I have a pretty long text it feels uncomfortable.

I've seen this answer by egreg and it works almost like I want. I'd like to specify the length as a first argument and pass the text without separating each letter individually as a second argument using xparse.

\spread{20em}{distribute this text}
\spread{5em}{word}

But, unfortunately, I was not managed to modify the code to make it work like I want. Could you help me with that?

antshar
  • 4,238
  • 9
  • 30
  • So, specifying the inter-letter spacing is not good enough? You need to be able to specify the overall-length? If so, you know that will preclude line wrapping. – Steven B. Segletes May 07 '20 at 13:21

2 Answers2

1

Using tokcycle, I provide two forms. In the first, the intercharacter spacing is specified. This is provided in the package example documentation. In the 2nd approach, using the syntax preferred by the OP, the user specifies the overall width of the string. Note that the target argument can contain formatting macros that are fully honored by \spread.

The disadvantage of \spread relative to \spaceouttext is that the former will not linebreak, whereas the latter will.

\documentclass{article}
\usepackage{tokcycle}
\newcommand\spaceouttext[2]{%
  \tokcycle{\addcytoks{##1\nobreak\hspace{#1}}}%
  {\processtoks{##1}}{\addcytoks{##1}}%
  {\addcytoks{##1\hspace{#1}}}{#2}%
  \the\cytoks\unskip}
\newcommand\spread[2]{%
  \tokcycle
  {\addcytoks{##1\hfill}}
  {\processtoks{##1}}
  {\addcytoks{##1}}
  {\addcytoks{##1\hfill}}{#2}%
  \makebox[#1]{\the\cytoks\unskip}%
}
\begin{document}
\spaceouttext{3pt plus 3pt}{This \textit{text \textbf{is} very} spaced
out}. Back to regular text.

\spaceouttext{1.5pt}{This \textit{text \textbf{is} somewhat} spaced
out}. Back to regular text.

\fbox{\spread{20em}{distribute \textit{this} text}}

\fbox{\spread{5em}{word}}
\end{document}

enter image description here

  • Thanks for spending time on that, but I don't want to use any additional packages apart from xparse. I just need to modify the linked answer, but I don't know how. – antshar May 07 '20 at 13:51
  • 2
    @antshar I have never understood the imperative to avoid packages, but, of course, that is your call. – Steven B. Segletes May 07 '20 at 14:01
1

First a simple solution that yields one space between letters and two between words:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\spreadtext}{mm}
 {
  \tl_set:Nx \l_tmpa_tl { #2 }
  \tl_replace_all:Nnn \l_tmpa_tl { ~ } { {} }
  \makebox[#1][s]
   {
    \skip_set:Nn \spaceskip { 0pt plus 1fill }
    \tl_map_inline:Nn \l_tmpa_tl { ##1 ~ } \unskip
   }
 }
\ExplSyntaxOff

\begin{document}

\spreadtext{20em}{distribute this text}

\spreadtext{5em}{word}

\end{document}

enter image description here

A slightly different one that has more space between words.

You can first split the input at spaces, then split each word at letters. Finally, typeset everything inserting a space between letters and a larger space between words.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\spreadtext}{mm}
 {
  \antshar_spread_text:nn { #1 } { #2 }
 }

\seq_new:N \l__antshar_spread_in_seq
\seq_new:N \l__antshar_spread_out_seq
\seq_new:N \l__antshar_spread_word_seq

\cs_new_protected:Nn \antshar_spread_text:nn
 {
  \seq_set_split:Nnn \l__antshar_spread_in_seq { ~ } { #2 }
  \seq_set_map:NNn \l__antshar_spread_out_seq \l__antshar_spread_in_seq { \antshar_spread_word:n { ##1 } }
  \makebox[#1][s]
   {
    \seq_use:Nn \l__antshar_spread_out_seq { \hspace{1em plus 0.5em minus 0.3em} }
   }
 }
\cs_new_protected:Nn \antshar_spread_word:n
 {
  \seq_set_split:Nnn \l__antshar_spread_word_seq { } { #1 }
  \seq_use:Nn \l__antshar_spread_word_seq { ~ }
 }
\ExplSyntaxOff

\begin{document}

\spreadtext{20em}{distribute this text}

\spreadtext{5em}{word}

\end{document}

enter image description here

A version that also copes with commands in the argument.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\spreadtext}{mm}
 {
  \antshar_spreadtext:nn { #1 } { #2 }
 }

\tl_new:N \l_antshar_spreadtext_tl

\cs_new_protected:Nn \__antshar_spreadtext_space:
 {
  \hspace{1em~plus~0.5em~minus~0.3em}
 }

\cs_new_protected:Nn \antshar_spreadtext:nn
 {
  \tl_set:Nn \l_antshar_spreadtext_tl { #2 }
  \regex_replace_all:nnN { \s } { \c{__antshar_spreadtext_space:} } \l_antshar_spreadtext_tl
  \regex_replace_all:nnN { (\w) } { \1 \  } \l_antshar_spreadtext_tl
  \regex_replace_all:nnN { \s(\c{.*}) } { \1  } \l_antshar_spreadtext_tl
  \tl_use:N \l_antshar_spreadtext_tl
 }
\ExplSyntaxOff

\begin{document}

\spreadtext{20em}{distribute \emph{this} text}

\spreadtext{5em}{word}

\end{document}

enter image description here

Without packages, but no command allowed:

\documentclass{article}

\makeatletter
\newcommand{\spreadtext}[2]{%
  \makebox[#1][s]{\spread@text#2\spread@text}%
}
\def\spread@text{\futurelet\next\spread@@text}
\def\spread@@text{%
  \ifx\next\spread@text
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\unskip\@gobble}%
  {\spread@@@text}%
}
\def\spread@@@text{%
  \ifx\next\@sptoken
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\spread@space}%
  {\spread@char}%
}
\def\spread@space#1{%
  \hspace{1em plus 0.5em minus 0.3em}%
  \spread@text#1%
}
\def\spread@char#1{#1 \spread@text}
\makeatother

\begin{document}

\spreadtext{20em}{distribute this text}

\spreadtext{5em}{word}

\end{document}
egreg
  • 1,121,712