How to create nonbreaking arbitrary length snakes for full-width justification?

This is a follow-up to the recent xkcd comic:

Here is a simple version without justification:

\documentclass{article}
\usepackage[text={1.2in,3in}]{geometry}
\raggedright
\begin{document}
\noindent Their famous paper on the relationship between deindustrialization and the growth of ecological \ldots
\end{document}


How to do a snake in LaTeX? To make the question more specific, how could I get LaTeX to automatically add a long underscore and then a letter 's' to each line that does not reach the end of the column? I am assuming with that knowledge I would be able to create the body and the head of the snake.

• +1 Interesting question, but maybe you could provide us with an MWE to start with and compare possible solutions? Commented May 4, 2016 at 6:59
• Care to explain in one line why snakes are used at all? Maybe a link to some resources? Commented May 5, 2016 at 7:39
• @Matsmath I'm not sure querying the ultimate authority of xkcd is allowed:-) Commented May 5, 2016 at 9:54
• Rats, I am not good at jokes. I thought this is some ancient typographic stuff :-). Commented May 5, 2016 at 9:59
• Some Arabic fonts can do kashida, which seems not entirely dissimilar...
– zwol
Commented May 5, 2016 at 20:49

this is lualatex only and draws a PDF line w points wide to pad out each line, if you are feeling artistic replace w 0 l by something that draws a snake of the same length

\documentclass{article}

\directlua{
PDF_LITERAL = node.subtype("pdf_literal")
WS=node.id("whatsit")
GLUE  = node.id("glue")
if n.subtype==9 then
print('  glue: ' .. n.subtype .. ' ' ..node.effective_glue(n,line) )
local w = node.effective_glue(n,line) / 65536
local sn = node.new(WS,PDF_LITERAL)
sn.data="0 G 0 0 m  " .. w .. " 0 l S 0 g"
line=node.insert_before(line,n,sn)
end
end
end
end
}
\def\snakefill{%
\directlua{
}%
}
\def\offsnakefill{%
\directlua{
luatexbase.remove_from_callback("post_linebreak_filter","snake fill")
}%
}

\begin{document}

\raggedright\parfillskip=0pt
\snakefill
one two three four five
one two three four five
one two three four fivexxxxxxxxxxxx
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
one two three four five
\end{document}


Or a version that's more snake like

% http://images.clipartpanda.com/cute-snake-clipart-black-and-white-30511wall.png

\directlua{
GLUE  = node.id("glue")
if n.subtype==9 then
print('  glue: ' .. n.subtype .. ' ' ..node.effective_glue(n,line) )
local w = node.effective_glue(n,line)
local sni = img.node{filename="cute-snake-clipart-black-and-white-30511wall.png"}
sni.height=500000
sni.width=w
line=node.insert_before(line,n,sni)
end
end
end
end
}

• Argh there goes snake, snake, it goes snake, oh it's a snake! :) If this link is not enough, click here :) Commented May 4, 2016 at 14:14
• I take it that texlive's current release (2015) is not sufficiently new for this? It complains about node.effective_glue being nil. To even get this far, I needed to \usepackage{luatextra}. Commented May 5, 2016 at 9:39
• @wrtlprnft ah I should check. was 9 \rightskip in the previous luatex (I couldn't get it to use a symbolic name unlike the GLUE and WHATSIT) You shouldn't need a package with tl2015 as long as it is updated so that the latex format is 2015/10/01 or later when more luatex support was added Commented May 5, 2016 at 9:46
• @wrtlprnft 9 was \rightskip in luatex 0.80, but effective_glue is apparently new sorry, I didn't spot that, you'd need to get the glue stretch settings off line and the lower level glue spec components from n and calculate the actual length. I may add that later if I get time. Commented May 5, 2016 at 9:52
• Ok, ditching gentoo's texlive packages and using the installer removed the need for luatexextra. Manually calculating effective_glue unfortunately goes beyond my nonexisting knowledge of lua(tex). Commented May 5, 2016 at 10:54

Really is not a snake, but ...

\documentclass{article}
\usepackage[text={1.2in,3in}]{geometry}
\hspace{-.3em minus -.2em}%
}\hfill$^{_\diamond}$\break}
\raggedright
\begin{document}
\noindent Their famous paper on the relationship between \snake
deindustrialization and \snake the growth of the main ecological  \snake
\end{document}


While I couldn't get David Carlisle's answer to work, I made a simple tielable vector version of the xkcd snake:

\documentclass{article}
\usepackage[text={1.2in,3in}]{geometry}
\usepackage[none]{hyphenat}

\directlua{
PDF_LITERAL = node.subtype("pdf_literal")
WS = node.id("whatsit")
GLUE = node.id("glue")
add_image = function(line, n, width, height, name)
local sni = img.node{filename=name}
sni.height = height
sni.width = width
return node.insert_before(line,n,sni)
end
add_snake = function(line, n, width, height)
local segments_middle = 0;
local segments_wave = 0;
local unstretched_width = height * (1.0 + 1.5)

while unstretched_width + height * 1.5 < width do
unstretched_width = unstretched_width + height * 1.5
segments_wave = segments_wave + 1
end

while unstretched_width + height * 0.5 < width do
unstretched_width = unstretched_width + height * 0.5
segments_middle = segments_middle + 1
end

local stretch = width / unstretched_width

line = add_image(line, n, height * 1.0 * stretch, height, "snake_tail.pdf")

for i = 1, segments_wave do
line = add_image(line, n, height * 1.5 * stretch, height, "snake_wave.pdf")
end

for i = 1, segments_middle do
line = add_image(line, n, height * 0.5 * stretch, height, "snake_middle.pdf")
end

return line
end
local w = 1250000 % remove when effective_glue works!
if n.subtype == 9 then
%local w = node.effective_glue(n,line) / 65536
w = w * 1.1 % remove when effective_glue works!
local h = 500000
line = add_snake(line, n, w, 500000)
end
end
end
end
}
\def\snakefill{%
\directlua{
}%
}
\def\offsnakefill{%
\directlua{
luatexbase.remove_from_callback("post_linebreak_filter","snake fill")
}%
}

\begin{document}
\raggedright
\snakefill
\noindent Their famous paper on the relationship between deindustrialization and the growth of ecological\ldots\par
\noindent Their famous paper on the relationship between deindustrialization and the growth of ecological\ldots\par
\noindent Their famous paper on the relationship between deindustrialization and the growth of ecological\ldots\par
\end{document}


The snake images can be obtained from this gist. I'll update the post if I find a way to get effective_glue to work.

The snake could be improved. Also I never know how to do calculations with tikz coordinates, so I use zref instead.

Edit

Actually I think that my first example didn't work in all cases as the begin and end mark doesn't need to be at the same position even if they were on the same line. So it is better to test if the second mark is at the end of the line.

Some explanations: Unlike the other answers my text doesn't use \raggedright but is fully justificated. As is it currently the text has to be one one page, but as tikzmark stores the page number, tests for the page could be added. One has to add "\optsnake" everywhere but this could probably be done with luatex throught a callback.

\documentclass{article}
\textwidth=2.9cm
\usepackage[none]{hyphenat}
\usepackage{tikz}
\usepackage{zref-savepos}
\usetikzlibrary{tikzmark}
\usetikzlibrary{decorations.pathmorphing}

\newcounter{snakefill}
\newcommand\optsnake{%
\stepcounter{snakefill}%
\pgfmark{snakemarkbegin\the\value{snakefill}}%
\zsavepos{snakemarkbegin\the\value{snakefill}}%
\hfil
\zsavepos{snakemarkend\the\value{snakefill}}%
\pgfmark{snakemarkend\the\value{snakefill}}%
\penalty50
\hfilneg
}

\newcommand\printsnakes{%
\begin{tikzpicture}[overlay,remember picture]
\foreach \x in {1,...,\the\value{snakefill}}
{
\ifnum  \zposx{snakemarkend\x}=\zposx{snakeendofline}
\draw [->,decorate,decoration=snake] (pic cs:snakemarkbegin\x) -- (pic cs:snakemarkend\x);
\fi
}
\end{tikzpicture}}
\begin{document}
\mbox{}\hfill\zsavepos{snakeendofline}% get the end of line value

\noindent
on the \optsnake relationship \optsnake
between \optsnake
Deindustrialization \optsnake
on the \optsnake relationship \optsnake
between \optsnake
Deindustrialization \optsnake
on the blblb \optsnake relationship \optsnake
between \optsnake
Deindustrialization \optsnake

\printsnakes

\end{document}


• Since you draw a single snake, it is unclear to me whether your solution is automatic, or you need to do this magic every time you want to include a snake. Care to clarify your answer? Commented May 5, 2016 at 7:55
• @Matsmath: I edited my answer Commented May 5, 2016 at 11:44