2

I know it is possible to break long lines using the seqsplit package. The following code block shows a minimal working example.

\documentclass{article}

\usepackage[a4paper, showframe]{geometry}

\usepackage{seqsplit}

\begin{document} Without using seqsplit:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

Using seqsplit:

\seqsplit{% abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ} \end{document}

enter image description here

I have defined a macro that generates a string containing ASCII letters in random order called \randomStringAllASCIILetters (see code and screenshot below). The macro uses another macro called \prunelist which was extracted from this answer, it randomly obtains items from a list without repetitions.

\documentclass{article}

\usepackage{pgf}

\usepackage{forloop}

\pgfmathsetseed{\number\pdfrandomseed}

\makeatletter \def\prunelist#1{% \expandafter\edef\csname pgfmath@randomlist@#1\endcsname {\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}% \count@\pgfmath@randomtemp \loop \expandafter\let \csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname \csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname \ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax \advance\count@@ne \repeat} \makeatother

\newcounter{asciiLetters}

\newcommand\randomStringAllASCIILetters{ % "mylist" contains all ASCII letters in lowercase and % uppercase. The list was obtained using the following one-liner: % % $ python3 -c "import string; print(''.join([f'{{{x}}}' for x in string.ascii_letters]))" \pgfmathdeclarerandomlist{mylist}{{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}{M}{N}{O}{P}{Q}{R}{S}{T}{U}{V}{W}{X}{Y}{Z}} % The following loop iterates as many times as the number of % lowercase and uppercase ASCII letters. The length was obtained % using the following one-liner. % % $ python3 -c "import string; print(len(string.ascii_letters))" \forloop{asciiLetters}{0}{\value{asciiLetters}<52}{% \pgfmathrandomitem\z{mylist}\z\prunelist{mylist}% }% }

\begin{document} \par foo \par \randomStringAllASCIILetters \par bar \end{document}

enter image description here

The problem is that even though the string returned by the macro \randomStringAllASCIILetters is passed to \seqsplit, the long string returned by the macro is not broken in the correct positions, so it exceeds the page limits.

\documentclass{article}

\usepackage[a4paper, showframe]{geometry}

\usepackage{seqsplit}

\usepackage{pgf}

\usepackage{forloop}

\pgfmathsetseed{\number\pdfrandomseed}

\makeatletter \def\prunelist#1{% \expandafter\edef\csname pgfmath@randomlist@#1\endcsname {\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}% \count@\pgfmath@randomtemp \loop \expandafter\let \csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname \csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname \ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax \advance\count@@ne \repeat} \makeatother

\newcounter{asciiLetters}

\newcommand\randomStringAllASCIILetters{ % "mylist" contains all ASCII letters in lowercase and % uppercase. The list was obtained using the following one-liner: % % $ python3 -c "import string; print(''.join([f'{{{x}}}' for x in string.ascii_letters]))" \pgfmathdeclarerandomlist{mylist}{{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}{M}{N}{O}{P}{Q}{R}{S}{T}{U}{V}{W}{X}{Y}{Z}} % The following loop iterates as many times as the number of % lowercase and uppercase ASCII letters. The length was obtained % using the following one-liner. % % $ python3 -c "import string; print(len(string.ascii_letters))" \forloop{asciiLetters}{0}{\value{asciiLetters}<52}{% \pgfmathrandomitem\z{mylist}\z\prunelist{mylist}% }% }

\begin{document} \seqsplit{% abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}

\seqsplit{% \randomStringAllASCIILetters\ \randomStringAllASCIILetters\ \randomStringAllASCIILetters} \end{document}

I have compiled the above document multiple times and I have noticed that sometimes the page limit is exceeded, but sometimes it isn't.

In the following image, there is at least one character that exceeds the page limits.

enter image description here

In the following image, there is no character that exceeds the page limits.

enter image description here

The question

As we could see above, the string abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ is broken in the correct position by \seqsplit{...} so that the string doesn't exceed the page limit. However, the string returned by \randomStringAllASCIILetters exceeds the page limit even though it has been passed to \seqsplit{...}. Why does this happen?

rdrg109
  • 565

1 Answers1

4

\seqsplit iterates through the input adding space between each token to allow break points so ABC turns in to A B C (with smaller spaces).

Your input is not a sequence of letters but some complicated macros that generate such a list when typeset, so it is an input like

{\def\tmp{A}\edef\tmp{\tmp b}\edef\tmp{\tmp C}\tmp}

such a construct would typeset as AbC but iterating through those tokens and adding spaces would not do anything useful

As is often the case with pgf (see \pgfmathresult) you should design your macros to build up the list in a result macro that expands to a simple list of letters and then pass that to your splitter.


It is much easier to do this in expl3 without having to juggle the expansion requirements of pgf and seqsplit

You just want to generate a sequence from the initial list of ascii letters then on each call shuffle the list and iterate through adding a zero space break point between each letter.

enter image description here

\documentclass{article}

\ExplSyntaxOn \seq_new:N \l_rdrg_ascii_seq \seq_set_split:Nnn \l_rdrg_ascii_seq {} {abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ} \cs_new:Npn \rdg_ascii_suffle: { \seq_shuffle:N \l_rdrg_ascii_seq \seq_use:Nn \l_rdrg_ascii_seq {\hspace{0pt}} } \let\asciishuffle\rdg_ascii_suffle: \ExplSyntaxOff \begin{document}

\fbox{\parbox[t]{4cm}{ \begin{flushleft}

\asciishuffle

\bigskip

\asciishuffle

\bigskip

\asciishuffle

\end{flushleft}}}

\fbox{\parbox[t]{3cm}{ \begin{flushleft}

\asciishuffle

\bigskip

\asciishuffle

\bigskip

\asciishuffle

\end{flushleft}}}

\end{document}

David Carlisle
  • 757,742
  • 1
    Could you provide a minimal working example that shows how to "build up the list in a result macro that expands to a simple list of letters"? – rdrg109 Mar 17 '24 at 01:47
  • @rdrg109 I added an expl3 version to the answer – David Carlisle Mar 17 '24 at 10:02
  • Thanks for the minimal working example. I have used \asciishuffle and compiled this document multiple times and I have noticed that in some compilations, there are some characters that exceed the page limit (see this screenshot). How could I make \asciishuffle not to exceed page limits without having to use \parbox? – rdrg109 Mar 17 '24 at 14:07
  • @rdrg109 that has been answered before. In this answer I used flushleft and a fixed 0pt space (which I think is more reasonable) that image says you did not copy this answer and went back to justifed text so you need \sloppy (if there will always be an inter-word space to stretch) or change the sequence iterator to add letterspacing changing 0pt to 0pt plus 5pt or however much letter spacing you will tolerate – David Carlisle Mar 17 '24 at 14:14
  • @rdrg109 \parbox is nothing to do with this that is just to demonstrate two different widths on the same answer. But the flushleft is needed (or as I say you need to do some other adjustments) – David Carlisle Mar 17 '24 at 14:16
  • Thanks. "the letterspacing or however much letter spacing I will tolerate" depends on the font size. Am I right? – rdrg109 Mar 17 '24 at 14:28
  • @rdrg109 well you could do \hspace{0pt plus .1em} so specify the stretch with font dependent units, but as I say I would do as shown here and use 0pt and set it raggedright, or if your text width is wide enough that there is at least one space on each line use sloppypar instead of flushleft and then the space will stretch to justify rather than letterspacing the string – David Carlisle Mar 17 '24 at 16:58