summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/ducksay
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2018-09-21 21:06:08 +0000
committerKarl Berry <karl@freefriends.org>2018-09-21 21:06:08 +0000
commit0972697fc4429b971f75335c69d8a71b9999097e (patch)
tree6a2c508754362eee8c4e9be2f255da6f58d67529 /Master/texmf-dist/source/latex/ducksay
parentc240d190fee85a0d263ff59264eaf1d7c5cdbdab (diff)
ducksay (21sep18)
git-svn-id: svn://tug.org/texlive/trunk@48720 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/ducksay')
-rw-r--r--Master/texmf-dist/source/latex/ducksay/ducksay.dtx2291
1 files changed, 2291 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/ducksay/ducksay.dtx b/Master/texmf-dist/source/latex/ducksay/ducksay.dtx
new file mode 100644
index 00000000000..92de1ce5d73
--- /dev/null
+++ b/Master/texmf-dist/source/latex/ducksay/ducksay.dtx
@@ -0,0 +1,2291 @@
+% \iffalse meta-comment
+%
+% File: ducksay.dtx Copyright (C) 2017-2018 Jonathan P. Spratte
+%
+% This work may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this license or
+% (at your option) any later version. The latest version of this license is in
+% the file:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% Alternatively you can use this work under the terms of the GNU General Public
+% Licensce (GPL), version 3 or later, except that you might not redistribute it
+% under the same name if you change its functionality. Choose which of these
+% licenses fits your needs the best.
+%
+% ------------------------------------------------------------------------------
+%
+%<*driver>^^A>>>
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+\input l3docstrip.tex
+\askforoverwritefalse
+\preamble
+
+--------------------------------------------------------------
+ducksay -- cowsay for LaTeX
+E-mail: jspratte@yahoo.de
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+--------------------------------------------------------------
+
+Copyright (C) 2017-2018 Jonathan P. Spratte
+
+This work may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this license or
+(at your option) any later version. The latest version of this license is in
+the file:
+
+ http://www.latex-project.org/lppl.txt
+
+Alternatively you can use this work under the terms of the GNU General Public
+Licensce (GPL), version 3 or later, except that you might not redistribute it
+under the same name if you change its functionality. Choose which of these
+licenses fits your needs the best.
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Jonathan P. Spratte.
+
+This work consists of the file ducksay.dtx
+and the derived files ducksay.pdf
+ ducksay.sty
+ ducksay.code.v1.tex
+ ducksay.code.v2.tex and
+ ducksay.animals.tex.
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+\generate{\file{ducksay.sty}{\from{ducksay.dtx}{pkg}}}
+\generate{\file{ducksay.code.v1.tex}{\from{ducksay.dtx}{code.v1}}}
+\generate{\file{ducksay.code.v2.tex}{\from{ducksay.dtx}{code.v2}}}
+\generate{\file{ducksay.animals.tex}{\from{ducksay.dtx}{animals}}}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%
+\ProvidesFile{ducksay.dtx}
+ [\csname ducksay@date\endcsname\ cowsay for LaTeX]
+\documentclass{l3doc}
+\usepackage[version=2]{ducksay}
+\let\metaORIG\meta
+\protected\def\meta #1{\texttt{\metaORIG{#1}}}
+\DucksayOptions{arg=tab,msg-align=l,vpad=1}
+\renewcommand*\thefootnote{\fnsymbol{footnote}}
+\newcommand*\anml{\meta{animal}}
+\newcommand*\msg{\meta{message}}
+\newcommand*\PolesInfo
+ {%
+ \href{https://ctan.org/pkg/l3kernel}{\file{interface3.pdf}} and the
+ documentation of \href{https://ctan.org/pkg/xcoffins}{\pkg{xcoffins}} for
+ information about coffin poles.%
+ }
+\usepackage{enumitem}
+\newenvironment{options}[1][]
+ {%
+ \begin{description}
+ [
+ style=nextline
+ ,font=\normalfont\ttfamily
+ ,labelindent=-.5\marginparwidth
+ ,labelwidth=\dimexpr.5\marginparwidth-5pt\relax
+ ,labelsep*=5pt
+ ,leftmargin=!
+ ,#1
+ ]%
+ \renewenvironment{options}[1][]
+ {%
+ \begin{description}
+ [
+ style=nextline
+ ,font=\normalfont\ttfamily
+ ,#1
+ ]%
+ }
+ {\end{description}}%
+ }
+ {\end{description}}
+\makeatletter
+\newcommand*\availableAnimal[1]
+ {%
+ \@for\cs:=#1\do
+ {%
+ \ifx\cs\@empty\else
+ \rlap{\expandafter\ducksay\expandafter[\cs]{\cs}}\hfill\null\par%
+ \fi
+ }%
+ }
+\def\@oddfoot
+ {%
+ \null\hfil
+ \makebox[0pt][c]
+ {%
+ \smash
+ {%
+ \ducksay
+ [dog,body=\tiny,wd=3,msg-align=c,out-v=T,MSG=\normalsize,vpad=0]
+ {\thepage}%
+ }%
+ }%
+ \hfil\null
+ }
+\let\@evenfoot\@oddfoot
+\def\@oddhead
+ {%
+ \null\hfill\firstmark
+ }
+\let\@evenhead\@oddhead
+\newcommand\DocImp{}
+\newcommand\SetVersion[1]
+ {%
+ \clearpage
+ \if\relax\detokenize{#1}\relax
+ \markboth{}{}%
+ \else
+ \markboth{}{\DocImp\ of Version #1}%
+ \fi
+ }
+\makeatother
+\setcounter{secnumdepth}{5}
+\setcounter{tocdepth}{5}
+\NewDocumentCommand \tocmsg {}
+ {%
+ \marginpar
+ {%
+ \tiny
+ \hfil
+ \makebox[.\marginparwidth][l]
+ {%
+ \ducksay[head-in,MSG=\footnotesize,msg-align=c,align=t]
+ {It's always\\good to\\keep the\\overview!}%
+ }%
+ }%
+ \unskip
+ }
+\addtocontents{toc}{\protect\tocmsg\vspace*{-\baselineskip}}
+\newcommand*\closingpage%>>>
+ {%
+ \clearpage
+ \thispagestyle{empty}
+ \bgroup
+ \Huge
+ \null\vfill
+ \centering
+ \makebox[0pt]{\duckthink{Who's gonna use it anyway?}}
+ \vfill
+ \hfill
+ \smash
+ {%
+ \footnotesize
+ \ducksay[small-yoda,wd=39,ht=3,msg-align=c,rel-align=r]
+ {Hosted at\\\url{https://github.com/Skillmon/ltx_ducksay}\\it is.}%
+ }
+ \egroup
+ \clearpage
+ }%<<<
+\begin{document}
+ \DocInput{ducksay.dtx}
+\end{document}
+%</driver>^^A<<<
+%<*pkg>^^A>>>
+\NeedsTeXFormat{LaTeX2e}
+\RequirePackage{xparse,l3keys2e}
+
+\def\ducksay@version{2.0}
+\def\ducksay@date{2018/09/21}
+
+\ProvidesExplPackage
+ {ducksay} {\ducksay@date}
+ {\ducksay@version} {cowsay for LaTeX}
+
+%</pkg>
+%^^A<<<
+% \fi
+%
+% \begin{titlepage}^^A>>>
+% \DucksayOptions{vpad=0}%
+% \makeatletter
+% \centering
+% \Large
+% \ducksay[duck,MSG=\huge,msg-align=c]{This is\\\pkg{ducksay}!}\\
+% \vfill
+% \normalsize
+% \hspace*{-2cm}
+% \ducksay[cow,MSG=\large,body-mirrored,body-align=r,msg-to-body=hc,out-h=r]
+% {v\ducksay@version}\\
+% \small
+% \vspace*{-5cm}\hspace*{5cm}
+% \ducksay[small-duck,MSG=\normalsize]{But which Version?}
+% \null\hfil
+% \vspace{2cm}
+% \vfill
+% \vfill
+% \hspace*{-0cm}
+% \large
+% \smash{%
+% \ducksay[r2d2,MSG=\large,body-mirrored,msg-to-body=hc,body-to-msg=r]
+% {by Jonathan P. Spratte}}
+% \small
+% \ducksay[hedgehog,MSG=\normalsize]{Today is \ducksay@date}
+% \makeatother
+% \end{titlepage}^^A<<<
+% \tableofcontents
+%
+% \begin{documentation}^^A>>>
+%
+% \section{Documentation}\def\DocImp{Documentation}%
+%
+% \subsection{Downward Compatibility Issues}
+% \marginpar^^A>>>
+% {
+% \tiny
+% \ducksay[snail,MSG=\footnotesize,align=t]{Yep, I screwed up!}
+% }^^A<<<
+%
+% \begin{itemize}
+% \item Versions prior to v2.0 did use a regular expression for the option
+% |ligatures|, see \autoref{sec:options} for more on this issue. With v2.0
+% I do refer to the package's version, not the code variant which can be
+% selected with the |version| option.
+% \item In a document created with package versions prior to v2.0 you'll have
+% to specify the option |version=1| in newer versions to make those old
+% documents behave like they used to.
+% \end{itemize}
+%
+% \subsection{Shared between versions}
+%
+% \subsubsection{Macros}^^A>>>
+% \marginpar^^A>>>
+% {
+% \tiny
+% \hfill
+% \ducksay[bunny,MSG=\footnotesize,align=t,body-mirrored]
+% {Macros for everyone!}
+% }^^A<<<
+%
+% A careful reader might notice that in the below list of macros there is no
+% \cs{ducksay} and no \cs{duckthink} contained. This is due to differences
+% between the two usable code variants (see the |version| key in
+% \autoref{sec:options} for the code variants, \autoref{sec:macros1} and
+% \autoref{sec:macros2} for descriptions of the two macros).
+%
+% \begin{function}{\DefaultAnimal}^^A>>>
+% \begin{syntax}
+% \cs{DefaultAnimal}\marg{animal}
+% \end{syntax}
+% use the \anml\ if none is given in the optional argument to \cs{ducksay}
+% or \cs{duckthink}. Package default is |duck|.
+% \end{function}^^A<<<
+%
+% \begin{function}{\DucksayOptions}^^A>>>
+% \begin{syntax}
+% \cs{DucksayOptions}\marg{options}
+% \end{syntax}
+% set the defaults to the keys described in \autoref{sec:options},
+% \autoref{sec:options1} and \autoref{sec:options2}. Don't use an \anml\ here,
+% it has no effect.
+% \end{function}^^A<<<
+%
+% \begin{function}{\AddAnimal}^^A>>>
+% \begin{syntax}
+% \cs{AddAnimal}\meta{*}\marg{animal}\meta{ascii-art}
+% \end{syntax}
+% adds \anml\ to the known animals. \meta{ascii-art} is multi-line verbatim
+% and therefore should be delimited either by matching braces or by anything
+% that works for \cs{verb}. If the star is given \anml\ is the new default.
+% One space is added to the begin of \anml\ (compensating the opening symbol).
+% For example, |snowman| is added with:\\[1ex]
+% \begin{minipage}{\linewidth}
+%\begin{verbatim}
+% \AddAnimal{snowman}
+% { \
+% \ _[_]_
+% (")
+% >-( : )-<
+% (__:__)}
+%\end{verbatim}
+% \end{minipage}
+% It is not checked whether the animal already exists, you could therefore
+% redefine existing animals with this macro.\\
+% The symbols signalizing the speech (in the |snowman| example above the two
+% backslashes) should at most be used in the first three lines, as they get
+% replaced by |O| and |o| for \cs{duckthink}. They also shouldn't be preceded
+% by anything other than a space in that line.
+% \end{function}^^A<<<
+%
+% \begin{function}{\AddColoredAnimal}^^A>>>
+% \begin{syntax}
+% \cs{AddColoredAnimal}\meta{*}\marg{animal}\meta{ascii-art}
+% \end{syntax}
+% It does the same as \cs{AddAnimal} but allows three different colouring
+% syntaxes. You can use \cs{textcolor} in the \meta{ascii-art} with the syntax
+% \texttt{\cs{textcolor}\marg{color}\marg{text}}. Note that you can't
+% use braces in the arguments of \cs{textcolor}.\\
+% You can also use a delimited \cs{color} of the form
+% \texttt{\cs{bgroup}\cs{color}\marg{color}\meta{text}\cs{egroup}}, a space
+% after that |\egroup| will be considered a space in the output, you don't
+% have to leave a space after the |\egroup| (so
+% |\bgroup\color{red}RedText\egroupOtherText| is valid syntax). You can't nest
+% delimited \cs{color}s.\\
+% Also you can use an undelimited \cs{color}. It affects anything until the
+% end of the current line (or, if used inside of the \meta{text} of an
+% delimited \cs{color}, anything until the end of that delimited \cs{color}'s
+% \meta{text}). The syntax would be \cs{color}\marg{color}.\\
+% The package doesn't load anything providing those colouring commands for you
+% and it doesn't provide any coloured animals. The parsing is done using
+% regular expressions provided by \LaTeX3. It is therefore slower than the
+% normal \cs{AddAnimal}.
+% \end{function}^^A<<<
+%
+%^^A<<<
+%
+% \subsubsection{Options}\label{sec:options}^^A>>>
+% \marginpar
+% {%
+% \vspace*{-2em}\tiny
+% \ducksay[pig,MSG=\footnotesize,align=t]{Options.\\For every occasion}%
+% }
+% The following options are available independent on the used code variant (the
+% value of the |version| key). They might be used as package options -- unless
+% otherwise specified -- or used in the macros \cs{DucksayOptions}, \cs{ducksay}
+% and \cs{duckthink} -- again unless otherwise specified. Some options might be
+% accessible in both code variants but do slightly different things. If that's
+% the case they will be explained in \autoref{sec:options1} and
+% \autoref{sec:options2} for |version| 1 and 2, respectively.
+% \begin{options}
+% \item[version=\meta{number}]
+% With this you can choose the code variant to be used. Currently |1| and
+% |2| are available. This can be set only during package load time. For a
+% dedicated description of each version look into \autoref{sec:v1} and
+% \autoref{sec:v2}. The package author would choose |version=2|, the other
+% version is mostly for legacy reasons. The default is |2|.
+% \item[\anml]
+% One of the animals listed in \autoref{sec:animals} or any of the ones
+% added with \cs{AddAnimal}. Not useable as package option. Also don't use
+% it in \cs{DucksayOptions}, it'll break the default animal selection.
+% \item[animal=\anml]
+% Locally sets the default animal. Note that \cs{ducksay} and \cs{duckthink}
+% do digest their options inside of a group, so it just results in
+% a longer alternative to the use of \anml\ if used in their options.
+% \item[ligatures=\meta{token list}]
+% each token you don't want to form ligatures during \cs{AddAnimal} should
+% be contained in this list. All of them get enclosed by grouping |{| and
+% |}| so that they can't form ligatures. Giving no argument (or an empty
+% one) might enhance compilation speed by disabling this replacement. The
+% formation of ligatures was only observed in combination with
+% \verb|\usepackage[T1]{fontenc}| by the author of this package. Therefore
+% giving the option |ligatures| without an argument might enhance the
+% compilation speed for you without any drawbacks. Initially this is set to
+% \texttt{\csuse{\detokenize{l_ducksay_ligatures_tl}}}.\\
+% \textbf{Note:} In earlier releases this option's expected argument was a
+% regular expression. This means that this option is not fully
+% downward compatible with older versions. The speed gain however seems
+% worth it (and I hope the affected documents are few).
+% \item[add-think=\meta{bool}]
+% by default the animals for \cs{duckthink} are not created during package
+% load time, but only when they are really used -- but then they are created
+% globally so it just has to be done once. This is done because they rely on
+% a rather slow regular expression. If you set this key to |true| each
+% \cs{AddAnimal} will also create the corresponding \cs{duckthink} variant
+% immediately.
+% \end{options}
+%^^A<<<
+%
+% \SetVersion{1}%
+% \subsection{Version 1}\label{sec:v1}
+%
+% \subsubsection{Introduction}
+%
+% This version is included for legacy support (old documents should behave the
+% same without any change to them -- except the usage of |version=1| as an
+% option. For the bleeding edge version of \pkg{ducksay} skip this subsection
+% and read \autoref{sec:v2}.
+%
+% \subsubsection{Macros}\label{sec:macros1}^^A>>>
+% \marginpar
+% {%
+% \rlap
+% {%
+% \tiny
+% \ducksay[yoda,MSG=\footnotesize,align=t]{Use those, you might}%
+% }%
+% }
+% The following is the description of macros which differ in behaviour from
+% those of version 2.
+%
+% \begin{function}{\ducksay}^^A>>>
+% \begin{syntax}
+% \cs{ducksay}\oarg{options}\marg{message}
+% \end{syntax}
+% options might include any of the options described in \autoref{sec:options}
+% and \autoref{sec:options1} if not otherwise specified. Prints an \anml\
+% saying \msg. \msg\ is not read in verbatim. Multi-line \msg s are possible
+% using |\\|. |\\| should not be contained in a macro definition but at
+% toplevel. Else use the option |ht|.
+% \end{function}^^A<<<
+%
+% \begin{function}{\duckthink}^^A>>>
+% \begin{syntax}
+% \cs{duckthink}\oarg{options}\marg{message}
+% \end{syntax}
+% options might include any of the options described in \autoref{sec:options}
+% and \autoref{sec:options1} if not otherwise specified. Prints an \anml\
+% thinking \msg. \msg\ is not read in verbatim. It is implemented using
+% regular expressions replacing a |\| which is only preceded by |\s*| in the
+% first three lines with |O| and |o|. It is therefore slower than
+% \cs{ducksay}. Multi-line \msg s are possible using |\\|. |\\| should not be
+% contained in a macro definition but at toplevel. Else use the option |ht|.
+% \end{function}^^A<<<
+%^^A<<<
+%
+% \subsubsection{Options}\label{sec:options1}^^A>>>
+% \marginpar
+% {%
+% \vspace*{-2em}\tiny
+% \hfill
+% \ducksay[hedgehog,MSG=\footnotesize,align=t]{Everyone likes\\options}%
+% }
+% The following options are available to \cs{ducksay}, \cs{duckthink}, and
+% \cs{DucksayOptions} and if not otherwise specified also as package options:
+% \begin{options}
+% \item[bubble=\meta{code}]
+% use \meta{code} in a group right before the bubble (for font switches).
+% Might be used as a package option but not all control sequences work out
+% of the box there.
+% \item[body=\meta{code}]
+% use \meta{code} in a group right before the body (meaning the \anml).
+% Might be used as a package option but not all control sequences work out
+% of the box there. E.g.\@ to right-align the \anml\ to the bubble, use
+% \verb|body=\hfill|.
+% \item[align=\meta{valign}]
+% use \meta{valign} as the vertical alignment specifier given to the
+% \env{tabular} which is around the contents of \cs{ducksay} and
+% \cs{duckthink}.
+% \item[msg-align=\meta{halign}]
+% use \meta{halign} for alignment of the rows of multi-line \msg s. It
+% should match a \texttt{tabular} column specifier. Default is |l|. It only
+% affects the contents of the speech bubble not the bubble.
+% \item[rel-align=\meta{column}]
+% use \meta{column} for alignment of the bubble and the body. It should
+% match a \env{tabular} column specifier. Default is |l|.
+% \item[wd=\meta{count}]
+% in order to detect the width the \msg\ is expanded. This might not work
+% out for some commands (e.g.\@ \cs{url} from \pkg{hyperref}). If you
+% specify the width using |wd| the \msg\ is not expanded and
+% therefore the command \emph{might} work out. \meta{count} should be the
+% character count.
+% \item[ht=\meta{count}]
+% you might explicitly set the height (the row count) of the \msg. This only
+% has an effect if you also specify |wd|.
+% \end{options}
+%^^A<<<
+%
+% \subsubsection{Defects}^^A>>>
+% \begingroup
+% \reversemarginpar
+% \marginpar
+% {\tiny\hfill\ducksay[frog,MSG=\footnotesize,align=t]{Ohh, no!}}
+% \endgroup
+% \begin{itemize}
+% \item no automatic line wrapping
+% \end{itemize}^^A<<<
+%
+% \SetVersion{2}%
+% \subsection{Version 2}\label{sec:v2}
+% \marginpar^^A>>>
+% {
+% \fontsize{3.5pt}{3.5pt}\selectfont
+% \ducksay[unicorn,MSG=\footnotesize,align=t]{Here's all the good stuff!}
+% }^^A<<<
+%
+% \subsubsection{Introduction}^^A>>>
+%
+% Version 2 is the current version of \pkg{ducksay}. It features automatic line
+% wrapping (if you specify a fixed width) and in general more options (with some
+% nasty argument parsing).
+%
+% If you're already used to version 1 you should note one important thing: You
+% should only specify the |version|, the |ligatures| and |add-think| during
+% package load time as arguments to \cs{usepackage}. The other keys might not
+% work or do unintended things and only don't throw errors or warnings because
+% of the legacy support of version 1.
+%
+%^^A<<<
+%
+% \subsubsection{Macros}\label{sec:macros2}^^A>>>
+% \marginpar^^A>>>
+% {
+% \tiny
+% \ducksay[duck-family,MSG=\footnotesize,align=t,body-mirrored]
+% {Look at those, kids!}
+% }^^A<<<
+%
+% The following is the description of macros which differ in behaviour from
+% those of version 1.
+%
+% \begin{function}{\ducksay}^^A>>>
+% \begin{syntax}
+% \cs{ducksay}\oarg{options}\marg{message}
+% \end{syntax}
+% options might include any of the options described in \autoref{sec:options}
+% and \autoref{sec:options2} if not otherwise specified. Prints an \anml\
+% saying \msg.\\
+% The \msg\ can be read in in four different ways. For an explanation of the
+% \msg\ reading see the description of the |arg| key in
+% \autoref{sec:options2}.\\
+% The height and width of the message is determined by measuring its
+% dimensions and the bubble will be set accordingly. The box surrounding the
+% message will be placed both horizontally and vertically centred inside of
+% the bubble. The output utilizes \LaTeX3's coffin mechanism described in
+% \href{https://ctan.org/pkg/l3kernel}{\file{interface3.pdf}} and the
+% documentation of \href{https://ctan.org/pkg/xcoffins}{\pkg{xcoffins}}.
+% \end{function}^^A<<<
+%
+% \begin{function}{\duckthink}^^A>>>
+% \begin{syntax}
+% \cs{duckthink}\oarg{options}\marg{message}
+% \end{syntax}
+% The only difference to \cs{ducksay} is that in \cs{duckthink} the \anml s
+% think the \msg\ and don't say it.\\
+% It is implemented using regular expressions replacing a |\| which is only
+% preceded by |\s*| (any number of space tokens) in the first three lines with
+% |O| and |o|. It's first use per \anml\ might therefore be slower than
+% \cs{ducksay} depending on the |add-think| key (see its description in
+% \autoref{sec:options}).
+% \end{function}^^A<<<
+%
+%^^A<<<
+%
+% \subsubsection{Options}\label{sec:options2}^^A>>>
+% \marginpar^^A>>>
+% {
+% \tiny
+% \hfill\ducksay[small-rabbit,MSG=\footnotesize,align=t]
+% {Fast, use options!}
+% }^^A<<<
+% In version 2 the following options are available. Keep in mind that you
+% shouldn't use them during package load time but in the arguments of
+% \cs{ducksay}, \cs{duckthink} or \cs{DucksayOptions}.
+% \begin{options}
+% \item[arg=\meta{choice}]
+% specifies how the \msg\ argument of \cs{ducksay} and \cs{duckthink} should
+% be read in. Available options are |box|, |tab| and |tab*|:
+% \begin{options}
+% \item[box]
+% the argument is read in either as a \cs{hbox} or a \cs{vbox} (the
+% latter if a fixed width is specified with either |wd| or |wd*|). Note
+% that in this mode any arguments relying on category code changes like
+% e.g.\@ \cs{verb} will work (provided that you don't use \cs{ducksay} or
+% \cs{duckthink} inside of an argument of another macro of course).
+% \item[tab]
+% the argument is read in as the contents of a \env{tabular}. Note that
+% in this mode any arguments relying on category code changes like
+% e.g.\@ \cs{verb} will \emph{not} work. This mode comes closest to the
+% behaviour of version 1 of \pkg{ducksay}.
+% \item[tab*]
+% the argument is read in as the contents of a \env{tabular}. However it
+% is read in verbatim and uses \cs{scantokens} to rescan the argument.
+% Note that in this mode any arguments relying on category code changes
+% like e.g.\@ \cs{verb} will work. You can't use \cs{ducksay} or
+% \cs{duckthink} as an argument to another macro in this mode however.
+% \end{options}
+% \item[b]
+% shortcut for |out-v=b|.
+% \item[body=\meta{font}]
+% add \meta{font} to the font definitions in use to typeset the \anml's
+% body.
+% \item[body*=\meta{font}]
+% clear any definitions previously made (including the package default) and
+% set the font definitions in use to typeset the \anml's body to
+% \meta{font}. The package default is \cs{verbatim@font}. In addition
+% \cs{frenchspacing} will always be used prior to the defined \meta{font}.
+% \item[body-align=\meta{choice}]
+% sets the relative alignment of the \anml\ to the \msg. Possible choices
+% are |l|, |c| and |r|. For |l| the \anml\ is flushed to the left of the
+% \msg, for |c| it is centred and for |r| it is flushed right. More fine
+% grained control over the alignment can be obtained with the keys
+% |msg-to-body|, |body-to-msg|, |body-x| and |body-y|. Package default is
+% |l|.
+% \item[body-mirrored=\meta{bool}]
+% if set true the \anml\ will be mirrored along its vertical centre axis.
+% Package default is |false|. If you set it |true| you'll most likely need
+% to manually adjust the alignment of the body with one or more of the
+% keys |body-align|, |body-to-msg|, |msg-to-body|, |body-x| and |body-y|.
+% \item[body-to-msg=\meta{pole}]
+% defines the horizontal coffin \meta{pole} to be used for the placement of
+% the \anml\ beneath the \msg. See \PolesInfo.
+% \item[body-x=\meta{dimen}]
+% defines a horizontal offset of \meta{dimen} length of the \anml\ from its
+% placement beneath the \msg.
+% \item[body-y=\meta{dimen}]
+% defines a vertical offset of \meta{dimen} length of the \anml\ from its
+% placement beneath the \msg.
+% \item[bubble=\meta{font}]
+% add \meta{font} to the font definitions in use to typeset the bubble. This
+% does not affect the \msg\ only the bubble put around it.
+% \item[bubble*=\meta{font}]
+% clear any definitions previously made (including the package default) and
+% set the font definitions in use to typeset the bubble to \meta{font}. This
+% does not affect the \msg\ only the bubble put around it. The package
+% default is \cs{verbatim@font}.
+% \item[bubble-bot-kern=\meta{dimen}]
+% specifies a vertical offset of the placement of the lower border of the
+% bubble from the bottom of the left and right borders.
+% \item[bubble-delim-left-1=\meta{token list}]
+% the left delimiter used if only one line of delimiters is needed. Package
+% default is |(|.
+% \item[bubble-delim-left-2=\meta{token list}]
+% the upper most left delimiter used if more than one line of delimiters is
+% needed. Package default is |/|.
+% \item[bubble-delim-left-3=\meta{token list}]
+% the left delimiters used to fill the gap if more than two lines of
+% delimiters are needed. Package default is \verb+|+.
+% \item[bubble-delim-left-4=\meta{token list}]
+% the lower most left delimiters used if more than one line of delimiters is
+% needed. Package default is |\|.
+% \item[bubble-delim-right-1=\meta{token list}]
+% the right delimiter used if only one line of delimiters is needed. Package
+% default is |)|.
+% \item[bubble-delim-right-2=\meta{token list}]
+% the upper most right delimiter used if more than one line of delimiters is
+% needed. Package default is |\|.
+% \item[bubble-delim-right-3=\meta{token list}]
+% the right delimiters used to fill the gap if more than two lines of
+% delimiters are needed. Package default is \verb+|+.
+% \item[bubble-delim-right-4=\meta{token list}]
+% the lower most right delimiters used if more than one line of delimiters
+% is needed. Package default is |/|.
+% \item[bubble-delim-top=\meta{token list}]
+% the delimiter used to create the top and bottom border of the bubble. The
+% package default is |{-}| (the braces are important to suppress ligatures
+% here).
+% \item[bubble-side-kern=\meta{dimen}]
+% specifies the kerning used to move the sideways delimiters added to fill
+% the gap for more than two lines of bubble height. (the left one is moved
+% to the left, the right one to the right)
+% \item[bubble-top-kern=\meta{dimen}]
+% specifies a vertical offset of the placement of the upper border of the
+% bubble from the top of the left and right borders.
+% \item[c]
+% shortcut for |out-v=vc|.
+% \item[col=\meta{column}]
+% specifies the used column specifier used for the \msg\ enclosing
+% \env{tabular} for |arg=tab| and |arg=tab*|. Has precedence over
+% |msg-align|. You can also use more than one column this way:
+% |\ducksay[arg=tab,col=cc]{ You & can \\ do & it }| would be valid syntax.
+% \item[hpad=\meta{count}]
+% Add \meta{count} times more |bubble-delim-top| instances than necassary to
+% the upper and lower border of the bubble. Package default is 2.
+% \item[ht=\meta{count}]
+% specifies a minimum height (in lines) of the \msg. The lines' count is
+% that of the needed lines of the horizontal bubble delimiters. If the
+% count of the actually needed lines is smaller than the specified
+% \meta{count}, \meta{count} lines will be used. Else the required lines
+% will be used.
+% \item[ignore-body=\meta{bool}]
+% If set |true| the \anml's body will be added to the output but it will not
+% contribute to the bounding box (so will not take up any space).
+% \item[msg=\meta{font}]
+% add \meta{font} to the font definitions in use to typeset the \msg.
+% \item[msg*=\meta{font}]
+% clear any definitions previously made (including the package default) and
+% set the font definitions in use to typeset the \msg\ to \meta{font}. The
+% package default is \cs{verbatim@font}.
+% \item[MSG=\meta{font}]
+% same as \texttt{msg=\meta{font}, bubble=\meta{font}}.
+% \item[MSG*=\meta{font}]
+% same as \texttt{msg*=\meta{font}, bubble*=\meta{font}}.
+% \item[msg-align=\meta{choice}]
+% specifies the alignment of the \msg. Possible values are |l| for flushed
+% left, |c| for centred, |r| for flushed right and |j| for justified. If
+% |arg=tab| or |arg=tab*| the |j| choice is only available for fixed width
+% contents. Package default is |l|.
+% \item[msg-align-c=\meta{token list}]
+% set the \meta{token list} which is responsible to typeset the message
+% centred if the option |msg-align=c| is used. It is used independent of the
+% |arg| key. For |arg=tab| and |arg=tab*| the macro \cs{arraybackslash}
+% provided by \pkg{array} is used afterwards. The package default is
+% |\centering|. It might be useful if you want to use \pkg{ragged2e}'s
+% \cs{Centering} for example.
+% \item[msg-align-j=\meta{token list}]
+% set the \meta{token list} which is responsible to typeset the message
+% justified if the option |msg-align=j| is used. It is used independent of
+% the |arg| key. For |arg=tab| and |arg=tab*| the macro \cs{arraybackslash}
+% provided by \pkg{array} is used afterwards. The package default is
+% empty as justification is the default behaviour of contents of a |p|
+% column and of a \cs{vbox}. It might be useful if you want to use
+% \pkg{ragged2e}'s \cs{justifying} for example.
+% \item[msg-align-l=\meta{token list}]
+% set the \meta{token list} which is responsible to typeset the message
+% flushed left if the option |msg-align=l| is used. It is used independent
+% of the |arg| key. For |arg=tab| and |arg=tab*| the macro
+% \cs{arraybackslash} provided by \pkg{array} is used afterwards. The
+% package default is |\raggedright|. It might be useful if you want to use
+% \pkg{ragged2e}'s \cs{RaggedRight} for example.
+% \item[msg-align-r=\meta{token list}]
+% set the \meta{token list} which is responsible to typeset the message
+% flushed right if the option |msg-align=r| is used. It is used independent
+% of the |arg| key. For |arg=tab| and |arg=tab*| the macro
+% \cs{arraybackslash} provided by \pkg{array} is used afterwards. The
+% package default is |\raggedleft|. It might be useful if you want to use
+% \pkg{ragged2e}'s \cs{RaggedLeft} for example.
+% \item[msg-to-bubble=\meta{pole}]
+% defines the horizontal coffin \meta{pole} to be used as the reference
+% point for the placement of the \anml\ beneath the \msg. See \PolesInfo.
+% \item[none=\meta{bool}]
+% One could say this is a special animal. If |true| no animal body will be
+% used (resulting in just the speech bubble). Package default is of course
+% |false|.
+% \item[out-h=\meta{pole}]
+% defines the horizontal coffin \meta{pole} to be used as the anchor point
+% for the print out of the complete result of \cs{ducksay} and
+% \cs{duckthink}. See \PolesInfo.
+% \item[out-v=\meta{pole}]
+% defines the vertical coffin \meta{pole} to be used as the anchor point for
+% the print out of the complete result of \cs{ducksay} and \cs{duckthink}.
+% See \PolesInfo.
+% \item[out-x=\meta{dimen}]
+% specifies an additional horizontal offset of the print out of the complete
+% result of \cs{ducksay} and \cs{duckthink}.
+% \item[out-y=\meta{dimen}]
+% specifies an additional vertical offset of the print out of the complete
+% result of \cs{ducksay} and \cs{duckthink}
+% \item[t]
+% shortcut for |out-v=t|.
+% \item[vpad=\meta{count}]
+% add \meta{count} to the lines used for the bubble, resulting in
+% \meta{count} more lines than necessary to enclose the \msg\ inside of the
+% bubble.
+% \item[wd=\meta{count}]
+% specifies the width of the \msg\ to be fixed to \meta{count} times the
+% width of an upper case M in the \msg's font declaration. A value smaller
+% than 0 is considered deactivated, else the width is considered as fixed.
+% For a fixed width the argument of \cs{ducksay} and \cs{duckthink} is read
+% in as a \cs{vbox} for |arg=box| and the column definition uses a |p|-type
+% column for |arg=tab| and |arg=tab*|. If both |wd| is not smaller than 0
+% and |wd*| is not smaller than 0pt, |wd*| will take precedence.
+% \item[wd*=\meta{dimen}]
+% specifies the width of the \msg\ to be fixed to \meta{dimen}. A value
+% smaller than 0pt is considered deactivated, else the width is considered
+% as fixed. For a fixed width the argument of \cs{ducksay} and
+% \cs{duckthink} is read in as a \cs{vbox} for |arg=box| and the column
+% definition uses a |p|-type column for |arg=tab| and |arg=tab*|. If both
+% |wd| is not smaller than 0 and |wd*| is not smaller than 0pt, |wd*| will
+% take precedence.
+% \end{options}
+%
+%^^A<<<
+%
+% \SetVersion{}%
+% \subsection{Dependencies}^^A>>>
+% \marginpar
+% {%
+% \tiny
+% \rlap
+% {%
+% \ducksay
+% [
+% kangaroo,MSG=\footnotesize,align=t
+% ,body-mirrored,body-to-msg=r,msg-to-body=hc
+% ]
+% {We rely on you}%
+% }%
+% }
+% The package depends on the two packages \pkg{xparse} and \pkg{l3keys2e}
+% and all of their dependencies. Version 2 additionally depends on \pkg{array}.
+%^^A<<<
+%
+% \subsection{Available Animals}\label{sec:animals}^^A>>>
+% \marginpar
+% {%
+% \tiny
+% \hfill
+% \makebox[8em][r]
+% {%
+% \ducksay[whale,MSG=\footnotesize,align=t]
+% {I'm the\\new one.}%
+% }%
+% }
+% The following animals are provided by this package. I did not create them (but
+% altered some), they belong to their original creators.
+% \bgroup
+% \fontsize{6pt}{6pt}\selectfont
+% \parindent=0pt
+% \DucksayOptions{MSG=\footnotesize,vpad=0,arg=tab}
+% \begin{multicols}{2}
+% \availableAnimal{^^A>>>
+% ,duck^^A
+% ,small-duck^^A
+% ,duck-family^^A
+% ,small-rabbit^^A
+% ,squirrel^^A
+% ,cow^^A
+% ,tux^^A
+% ,head-in^^A
+% ,pig^^A
+% ,frog^^A
+% ,snowman^^A
+% ,bunny^^A
+% ,dragon^^A
+% ,sodomized^^A
+% ,hedgehog^^A
+% ,kangaroo^^A
+% ,dog^^A
+% ,rabbit^^A
+% ,snail^^A
+% ,whale^^A
+% ,unicorn^^A
+% }\end{multicols}\begin{multicols}{2}
+% \availableAnimal{^^A
+% ,r2d2^^A
+% ,vader^^A
+% ,yoda-head^^A
+% ,small-yoda^^A
+% ,yoda^^A
+% }^^A<<<
+% \end{multicols}
+% \egroup
+%^^A<<<
+%
+% \subsection{Miscellaneous}^^A>>>
+% \marginpar
+% {%
+% \rlap
+% {%
+% \tiny
+% \ducksay[squirrel,MSG=\footnotesize,align=t]{I'd choose WTFPL}%
+% }%
+% }
+% This package is distributed under the terms of the GPLv3 or later, or the LPPL
+% 1.3c or later, choose which ever license fits your needs the best.
+%
+% The package is hosted on \url{https://github.com/Skillmon/ltx_ducksay}, you
+% might report bugs there.
+%^^A<<<
+%
+% \end{documentation}^^A<<<
+%
+% \begin{implementation}^^A>>>
+%
+% \clearpage
+%
+% \SetVersion{}\def\DocImp{Implementation}%
+% \section{Implementation}^^A>>>
+% \marginpar
+% {%
+% \smash
+% {%
+% \tiny
+% \ducksay
+% [vader,MSG=\footnotesize,align=t,arg=tab,msg-align=c]
+% {%
+% Only rebel scum reads\\documentation!\\
+% Join the dark side,\\read the implementation.%
+% }%
+% }%
+% }
+%
+%^^A main file >>>
+% \begin{macrocode}
+%<*pkg>
+% \end{macrocode}
+%
+% \subsection{Shared between versions}^^A>>>
+%
+% \subsubsection{Variables}^^A>>>
+% \paragraph{Integers}
+% \begin{macrocode}
+\int_new:N \l_ducksay_msg_width_int
+\int_new:N \l_ducksay_msg_height_int
+% \end{macrocode}
+% \paragraph{Sequences}
+% \begin{macrocode}
+\seq_new:N \l_ducksay_msg_lines_seq
+% \end{macrocode}
+% \paragraph{Token lists}
+% \begin{macrocode}
+\tl_new:N \l_ducksay_say_or_think_tl
+\tl_new:N \l_ducksay_align_tl
+\tl_new:N \l_ducksay_msg_align_tl
+\tl_new:N \l_ducksay_animal_tl
+\tl_new:N \l_ducksay_body_tl
+\tl_new:N \l_ducksay_bubble_tl
+\tl_new:N \l_ducksay_tmpa_tl
+% \end{macrocode}
+% \paragraph{Boolean}
+% \begin{macrocode}
+\bool_new:N \l_ducksay_also_add_think_bool
+\bool_new:N \l_ducksay_version_one_bool
+\bool_new:N \l_ducksay_version_two_bool
+% \end{macrocode}
+% \paragraph{Boxes}
+% \begin{macrocode}
+\box_new:N \l_ducksay_tmpa_box
+% \end{macrocode}
+%
+%^^A<<<
+%
+% \subsubsection{Regular Expressions}^^A>>>
+% Regular expressions for \cs{duckthink}
+% \begin{macrocode}
+\regex_const:Nn \c_ducksay_first_regex { \A(.\s*)\\ }
+\regex_const:Nn \c_ducksay_second_regex { \A(.[^\c{null}]*\c{null}\s*)\\ }
+\regex_const:Nn \c_ducksay_third_regex {
+ \A(.[^\c{null}]*\c{null}[^\c{null}]*\c{null}\s*)\\ }
+\regex_const:Nn \c_ducksay_textcolor_regex
+ { \cO(?:\\textcolor\{(.*?)\}\{(.*?)\}) }
+\regex_const:Nn \c_ducksay_color_delim_regex
+ { \cO(?:\\bgroup\\color\{(.*?)\}(.*)\\egroup) }
+\regex_const:Nn \c_ducksay_color_regex
+ { \cO(?:\\color\{(.*?)\}) }
+% \end{macrocode}
+%^^A<<<
+%
+% \subsubsection{Messages}^^A>>>
+% \begin{macrocode}
+\msg_new:nnn { ducksay } { load-time-only }
+ { The~`#1`~key~is~to~be~used~only~during~package~load~time. }
+% \end{macrocode}
+%^^A<<<
+%
+% \subsubsection{Key-value setup}^^A>>>
+% \begin{macrocode}
+\keys_define:nn { ducksay }
+ {
+ ,bubble .tl_set:N = \l_ducksay_bubble_tl
+ ,body .tl_set:N = \l_ducksay_body_tl
+ ,align .tl_set:N = \l_ducksay_align_tl
+ ,align .value_required:n = true
+ ,wd .int_set:N = \l_ducksay_msg_width_int
+ ,wd .initial:n = -\c_max_int
+ ,wd .value_required:n = true
+ ,ht .int_set:N = \l_ducksay_msg_height_int
+ ,ht .initial:n = -\c_max_int
+ ,ht .value_required:n = true
+ ,animal .code:n =
+ { \keys_define:nn { ducksay } { default_animal .meta:n = { #1 } } }
+ ,animal .initial:n = duck
+ ,msg-align .tl_set:N = \l_ducksay_msg_align_tl
+ ,msg-align .initial:n = l
+ ,msg-align .value_required:n = true
+ ,rel-align .tl_set:N = \l_ducksay_rel_align_tl
+ ,rel-align .initial:n = l
+ ,rel-align .value_required:n = true
+ ,ligatures .tl_set:N = \l_ducksay_ligatures_tl
+ ,ligatures .initial:n = { `<>,'- }
+ ,add-think .bool_set:N = \l_ducksay_also_add_think_bool
+ ,version .choice:
+ ,version / 1 .code:n =
+ {
+ \bool_set_false:N \l_ducksay_version_two_bool
+ \bool_set_true:N \l_ducksay_version_one_bool
+ }
+ ,version / 2 .code:n =
+ {
+ \bool_set_false:N \l_ducksay_version_one_bool
+ \bool_set_true:N \l_ducksay_version_two_bool
+ }
+ ,version .initial:n = 2
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ProcessKeysOptions { ducksay }
+% \end{macrocode}
+%
+% Undefine the load-time-only keys
+% \begin{macrocode}
+\keys_define:nn { ducksay }
+ {
+ version .code:n = \msg_error:nnn { ducksay } { load-time-only } { version }
+ }
+% \end{macrocode}
+%
+%^^A<<<
+%
+% \subsubsection{Functions}^^A>>>
+%
+% \paragraph{Generating Variants of External Functions}^^A>>>
+%
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_if_eq:nnT { VnT }
+% \end{macrocode}
+%^^A<<<
+%
+% \paragraph{Internal}^^A>>>
+%
+% \begin{macro}{\ducksay_create_think_animal:n}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npn \ducksay_create_think_animal:n #1
+ {
+ \group_begin:
+ \tl_set_eq:Nc \l_ducksay_tmpa_tl { g_ducksay_animal_say_#1_tl }
+ \regex_replace_once:NnN \c_ducksay_first_regex { \1O } \l_ducksay_tmpa_tl
+ \regex_replace_once:NnN \c_ducksay_second_regex { \1o } \l_ducksay_tmpa_tl
+ \regex_replace_once:NnN \c_ducksay_third_regex { \1o } \l_ducksay_tmpa_tl
+ \tl_gset_eq:cN { g_ducksay_animal_think_#1_tl } \l_ducksay_tmpa_tl
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_replace_verb_newline:Nn}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npx \ducksay_replace_verb_newline:Nn #1 #2
+ {
+ \tl_replace_all:Nnn #1 { \char_generate:nn { 13 } { 12 } } { #2 }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_replace_verb_newline_newline:Nn}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npx \ducksay_replace_verb_newline_newline:Nn #1 #2
+ {
+ \tl_replace_all:Nnn #1
+ { \char_generate:nn { 13 } { 12 } \char_generate:nn { 13 } { 12 } } { #2 }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_process_verb_newline:nnn}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npn \ducksay_process_verb_newline:nnn #1 #2 #3
+ {
+ \tl_set:Nn \ProcessedArgument { #3 }
+ \ducksay_replace_verb_newline_newline:Nn \ProcessedArgument { #2 }
+ \ducksay_replace_verb_newline:Nn \ProcessedArgument { #1 }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_add_animal_inner:nn}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npn \ducksay_add_animal_inner:nn #1 #2
+ {
+ \tl_set:Nn \l_ducksay_tmpa_tl { \ #2 }
+ \tl_map_inline:Nn \l_ducksay_ligatures_tl
+ { \tl_replace_all:Nnn \l_ducksay_tmpa_tl { ##1 } { { ##1 } } }
+ \ducksay_replace_verb_newline:Nn \l_ducksay_tmpa_tl { \tabularnewline\null }
+ \tl_gset_eq:cN { g_ducksay_animal_say_#1_tl } \l_ducksay_tmpa_tl
+ \keys_define:nn { ducksay }
+ {
+ #1 .code:n =
+ {
+ \tl_if_exist:cF
+ { g_ducksay_animal_ \l_ducksay_say_or_think_tl _#1_tl }
+ { \ducksay_create_think_animal:n { #1 } }
+ \tl_set_eq:Nc \l_ducksay_animal_tl
+ { g_ducksay_animal_ \l_ducksay_say_or_think_tl _#1_tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+%^^A<<<
+%
+% \paragraph{Document level}^^A>>>
+%
+% \begin{macro}{\DefaultAnimal}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \DefaultAnimal { m }
+ {
+ \keys_define:nn { ducksay } { default_animal .meta:n = { #1 } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\DucksayOptions}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \DucksayOptions { m }
+ {
+ \keys_set:nn { ducksay } { #1 }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\AddAnimal}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \AddAnimal { s m +v }
+ {
+ \ducksay_add_animal_inner:nn { #2 } { #3 }
+ \bool_if:NT \l_ducksay_also_add_think_bool
+ { \ducksay_create_think_animal:n { #2 } }
+ \IfBooleanT{#1}
+ { \keys_define:nn { ducksay } { default_animal .meta:n = { #2 } } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\AddColoredAnimal}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \AddColoredAnimal { s m +v }
+ {
+ \ducksay_add_animal_inner:nn { #2 } { #3 }
+ \regex_replace_all:Nnc \c_ducksay_color_delim_regex
+ { \c{bgroup}\c{color}\cB\{\1\cE\}\2\c{egroup} }
+ { g_ducksay_animal_say_#2_tl }
+ \regex_replace_all:Nnc \c_ducksay_color_regex
+ { \c{color}\cB\{\1\cE\} }
+ { g_ducksay_animal_say_#2_tl }
+ \regex_replace_all:Nnc \c_ducksay_textcolor_regex
+ { \c{textcolor}\cB\{\1\cE\}\cB\{\2\cE\} }
+ { g_ducksay_animal_say_#2_tl }
+ \bool_if:NT \l_ducksay_also_add_think_bool
+ { \ducksay_create_think_animal:n { #2 } }
+ \IfBooleanT{#1}
+ { \keys_define:nn { ducksay } { default_animal .meta:n = { #2 } } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+%^^A<<<
+%
+%^^A<<<
+%
+% \subsubsection{Load the Correct Version and the Animals}^^A>>>
+% \begin{macrocode}
+\bool_if:NT \l_ducksay_version_one_bool
+ { \file_input:n { ducksay.code.v1.tex } }
+\bool_if:NT \l_ducksay_version_two_bool
+ { \file_input:n { ducksay.code.v2.tex } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+\input{ducksay.animals.tex}
+% \end{macrocode}
+%^^A<<<
+%
+%^^A<<<
+%
+% \begin{macrocode}
+%</pkg>
+% \end{macrocode}
+%^^A<<<
+%
+% \SetVersion{1}%
+% \subsection{Version 1}^^A>>>
+% \begin{macrocode}
+%<*code.v1>
+% \end{macrocode}
+%
+% \subsubsection{Functions}^^A>>>
+%
+% \paragraph{Internal}^^A>>>
+%
+% \begin{macro}{\ducksay_longest_line:n}^^A>>>
+% Calculate the length of the longest line
+% \begin{macrocode}
+\cs_new:Npn \ducksay_longest_line:n #1
+ {
+ \int_incr:N \l_ducksay_msg_height_int
+ \exp_args:NNx \tl_set:Nn \l_ducksay_tmpa_tl { #1 }
+ \regex_replace_all:nnN { \s } { \c { space } } \l_ducksay_tmpa_tl
+ \int_set:Nn \l_ducksay_msg_width_int
+ {
+ \int_max:nn
+ { \l_ducksay_msg_width_int } { \tl_count:N \l_ducksay_tmpa_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_open_bubble:}^^A>>>
+% Draw the opening bracket of the bubble
+% \begin{macrocode}
+\cs_new:Npn \ducksay_open_bubble:
+ {
+ \begin{tabular}{@{}l@{}}
+ \null\\
+ \int_compare:nNnTF { \l_ducksay_msg_height_int } = { 1 } { ( }
+ {
+ /
+ \int_step_inline:nnn
+ { 3 } { \l_ducksay_msg_height_int } { \\\kern-0.2em| }
+ \\\detokenize{\ }
+ }
+ \\[-1ex]\null
+ \end{tabular}
+ \begin{tabular}{@{}l@{}}
+ _\\
+ \int_step_inline:nnn { 2 } { \l_ducksay_msg_height_int } { \\ } \\[-1ex]
+ \mbox { - }
+ \end{tabular}
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_close_bubble:}^^A>>>
+% Draw the closing bracket of the bubble
+% \begin{macrocode}
+\cs_new:Npn \ducksay_close_bubble:
+ {
+ \begin{tabular}{@{}l@{}}
+ _\\
+ \int_step_inline:nnn { 2 } { \l_ducksay_msg_height_int } { \\ } \\[-1ex]
+ { - }
+ \end{tabular}
+ \begin{tabular}{@{}r@{}}
+ \null\\
+ \int_compare:nNnTF { \l_ducksay_msg_height_int } = { 1 }
+ { ) }
+ {
+ \detokenize {\ }
+ \int_step_inline:nnn
+ { 3 } { \l_ducksay_msg_height_int } { \\|\kern-0.2em }
+ \\/
+ }
+ \\[-1ex]\null
+ \end{tabular}
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_print_msg:nn}^^A>>>
+% Print out the message
+% \begin{macrocode}
+\cs_new:Npn \ducksay_print_msg:nn #1 #2
+ {
+ \begin{tabular}{@{} #2 @{}}
+ \int_step_inline:nn { \l_ducksay_msg_width_int } { _ } \\
+ #1\\[-1ex]
+ \int_step_inline:nn { \l_ducksay_msg_width_int } { { - } }
+ \end{tabular}
+ }
+\cs_generate_variant:Nn \ducksay_print_msg:nn { nV }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_print:nn}^^A>>>
+% Print out the whole thing
+% \begin{macrocode}
+\cs_new:Npn \ducksay_print:nn #1 #2
+ {
+ \int_compare:nNnTF { \l_ducksay_msg_width_int } < { 0 }
+ {
+ \int_zero:N \l_ducksay_msg_height_int
+ \seq_set_split:Nnn \l_ducksay_msg_lines_seq { \\ } { #1 }
+ \seq_map_function:NN \l_ducksay_msg_lines_seq \ducksay_longest_line:n
+ }
+ {
+ \int_compare:nNnT { \l_ducksay_msg_height_int } < { 0 }
+ {
+ \regex_count:nnN { \c { \\ } } { #1 } \l_ducksay_msg_height_int
+ \int_incr:N \l_ducksay_msg_height_int
+ }
+ }
+ \group_begin:
+ \frenchspacing
+ \verbatim@font
+ \@noligs
+ \begin{tabular}[\l_ducksay_align_tl]{@{}#2@{}}
+ \l_ducksay_bubble_tl
+ \begin{tabular}{@{}l@{}}
+ \ducksay_open_bubble:
+ \ducksay_print_msg:nV { #1 } \l_ducksay_msg_align_tl
+ \ducksay_close_bubble:
+ \end{tabular}\\
+ \l_ducksay_body_tl
+ \begin{tabular}{@{}l@{}}
+ \l_ducksay_animal_tl
+ \end{tabular}
+ \end{tabular}
+ \group_end:
+ }
+\cs_generate_variant:Nn \ducksay_print:nn { nV }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_prepare_say_and_think:n}^^A>>>
+% Reset some variables
+% \begin{macrocode}
+\cs_new:Npn \ducksay_prepare_say_and_think:n #1
+ {
+ \int_set:Nn \l_ducksay_msg_width_int { -\c_max_int }
+ \int_set:Nn \l_ducksay_msg_height_int { -\c_max_int }
+ \keys_set:nn { ducksay } { #1 }
+ \tl_if_empty:NT \l_ducksay_animal_tl
+ { \keys_set:nn { ducksay } { default_animal } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%^^A<<<
+%
+% \paragraph{Document level}^^A>>>
+%
+% \begin{macro}{\ducksay}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \ducksay { O{} m }
+ {
+ \group_begin:
+ \tl_set:Nn \l_ducksay_say_or_think_tl { say }
+ \ducksay_prepare_say_and_think:n { #1 }
+ \ducksay_print:nV { #2 } \l_ducksay_rel_align_tl
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\duckthink}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \duckthink { O{} m }
+ {
+ \group_begin:
+ \tl_set:Nn \l_ducksay_say_or_think_tl { think }
+ \ducksay_prepare_say_and_think:n { #1 }
+ \ducksay_print:nV { #2 } \l_ducksay_rel_align_tl
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%^^A<<<
+%
+%^^A<<<
+%
+% \begin{macrocode}
+%</code.v1>
+% \end{macrocode}^^A<<<
+%
+% \SetVersion{2}%
+% \subsection{Version 2}^^A>>>
+% \begin{macrocode}
+%<*code.v2>
+% \end{macrocode}
+%
+% Load the additional dependencies of version 2.
+% \begin{macrocode}
+\RequirePackage{array}
+% \end{macrocode}
+%
+% \subsubsection{Messages}^^A>>>
+% \begin{macrocode}
+\msg_new:nnn { ducksay } { justify~unavailable }
+ {
+ Justified~content~is~not~available~for~tabular~argument~mode~without~fixed~
+ width.~`l`~column~is~used~instead.
+ }
+\msg_new:nnn { ducksay } { unknown~message~alignment }
+ {
+ The~specified~message~alignment~`\exp_not:n { #1 }`~is~unknown.~
+ `l`~is~used~as~fallback.
+ }
+% \end{macrocode}
+%^^A<<<
+%
+% \subsubsection{Variables}^^A>>>
+%
+% \paragraph{Token Lists}
+% \begin{macrocode}
+\tl_new:N \l_ducksay_msg_align_vbox_tl
+% \end{macrocode}
+%
+% \paragraph{Boxes}
+% \begin{macrocode}
+\box_new:N \l_ducksay_msg_box
+% \end{macrocode}
+%
+% \paragraph{Bools}
+% \begin{macrocode}
+\bool_new:N \l_ducksay_eat_arg_box_bool
+\bool_new:N \l_ducksay_eat_arg_tab_verb_bool
+\bool_new:N \l_ducksay_mirrored_body_bool
+% \end{macrocode}
+%
+% \paragraph{Coffins}
+% \begin{macrocode}
+\coffin_new:N \l_ducksay_body_coffin
+\coffin_new:N \l_ducksay_bubble_close_coffin
+\coffin_new:N \l_ducksay_bubble_open_coffin
+\coffin_new:N \l_ducksay_bubble_top_coffin
+\coffin_new:N \l_ducksay_msg_coffin
+% \end{macrocode}
+%
+% \paragraph{Dimensions}
+% \begin{macrocode}
+\dim_new:N \l_ducksay_hpad_dim
+\dim_new:N \l_ducksay_bubble_bottom_kern_dim
+\dim_new:N \l_ducksay_bubble_top_kern_dim
+\dim_new:N \l_ducksay_msg_width_dim
+% \end{macrocode}
+%
+%^^A<<<
+%
+% \subsubsection{Options}^^A>>>
+%
+% \begin{macrocode}
+\keys_define:nn { ducksay }
+ {
+ ,arg .choice:
+ ,arg / box .code:n = \bool_set_true:N \l_ducksay_eat_arg_box_bool
+ ,arg / tab .code:n =
+ {
+ \bool_set_false:N \l_ducksay_eat_arg_box_bool
+ \bool_set_false:N \l_ducksay_eat_arg_tab_verb_bool
+ }
+ ,arg / tab* .code:n =
+ {
+ \bool_set_false:N \l_ducksay_eat_arg_box_bool
+ \bool_set_true:N \l_ducksay_eat_arg_tab_verb_bool
+ }
+ ,arg .initial:n = tab
+ ,wd* .dim_set:N = \l_ducksay_msg_width_dim
+ ,wd* .initial:n = -\c_max_dim
+ ,wd* .value_required:n = true
+ ,none .bool_set:N = \l_ducksay_no_body_bool
+ ,body-mirrored .bool_set:N = \l_ducksay_mirrored_body_bool
+ ,ignore-body .bool_set:N = \l_ducksay_ignored_body_bool
+ ,body-x .dim_set:N = \l_ducksay_body_x_offset_dim
+ ,body-x .value_required:n = true
+ ,body-y .dim_set:N = \l_ducksay_body_y_offset_dim
+ ,body-y .value_required:n = true
+ ,body-to-msg .tl_set:N = \l_ducksay_body_to_msg_align_body_tl
+ ,msg-to-body .tl_set:N = \l_ducksay_body_to_msg_align_msg_tl
+ ,body-align .choice:
+ ,body-align / l .meta:n = { body-to-msg = l , msg-to-body = l }
+ ,body-align / c .meta:n = { body-to-msg = hc , msg-to-body = hc }
+ ,body-align / r .meta:n = { body-to-msg = r , msg-to-body = r }
+ ,body-align .initial:n = l
+ ,msg-align .choice:
+ ,msg-align / l .code:n = { \tl_set:Nn \l_ducksay_msg_align_tl { l } }
+ ,msg-align / c .code:n = { \tl_set:Nn \l_ducksay_msg_align_tl { c } }
+ ,msg-align / r .code:n = { \tl_set:Nn \l_ducksay_msg_align_tl { r } }
+ ,msg-align / j .code:n = { \tl_set:Nn \l_ducksay_msg_align_tl { j } }
+ ,msg-align-l .tl_set:N = \l_ducksay_msg_align_l_tl
+ ,msg-align-l .initial:n = \raggedright
+ ,msg-align-c .tl_set:N = \l_ducksay_msg_align_c_tl
+ ,msg-align-c .initial:n = \centering
+ ,msg-align-r .tl_set:N = \l_ducksay_msg_align_r_tl
+ ,msg-align-r .initial:n = \raggedleft
+ ,msg-align-j .tl_set:N = \l_ducksay_msg_align_j_tl
+ ,msg-align-j .initial:n = {}
+ ,out-h .tl_set:N = \l_ducksay_output_h_pole_tl
+ ,out-h .initial:n = l
+ ,out-v .tl_set:N = \l_ducksay_output_v_pole_tl
+ ,out-v .initial:n = vc
+ ,out-x .dim_set:N = \l_ducksay_output_x_offset_dim
+ ,out-x .value_required:n = true
+ ,out-y .dim_set:N = \l_ducksay_output_y_offset_dim
+ ,out-y .value_required:n = true
+ ,t .meta:n = { out-v = t }
+ ,c .meta:n = { out-v = vc }
+ ,b .meta:n = { out-v = b }
+ ,body* .tl_set:N = \l_ducksay_body_fount_tl
+ ,msg* .tl_set:N = \l_ducksay_msg_fount_tl
+ ,bubble* .tl_set:N = \l_ducksay_bubble_fount_tl
+ ,body* .initial:n = \verbatim@font
+ ,msg* .initial:n = \verbatim@font
+ ,bubble* .initial:n = \verbatim@font
+ ,body .code:n = \tl_put_right:Nn \l_ducksay_body_fount_tl { #1 }
+ ,msg .code:n = \tl_put_right:Nn \l_ducksay_msg_fount_tl { #1 }
+ ,bubble .code:n = \tl_put_right:Nn \l_ducksay_bubble_fount_tl { #1 }
+ ,MSG .meta:n = { msg = #1 , bubble = #1 }
+ ,MSG* .meta:n = { msg* = #1 , bubble* = #1 }
+ ,hpad .int_set:N = \l_ducksay_hpad_int
+ ,hpad .initial:n = 2
+ ,hpad .value_required:n = true
+ ,vpad .int_set:N = \l_ducksay_vpad_int
+ ,vpad .value_required:n = true
+ ,col .tl_set:N = \l_ducksay_msg_tabular_column_tl
+ ,bubble-top-kern .tl_set:N = \l_ducksay_bubble_top_kern_tl
+ ,bubble-top-kern .initial:n = { -.5ex }
+ ,bubble-top-kern .value_required:n = true
+ ,bubble-bot-kern .tl_set:N = \l_ducksay_bubble_bottom_kern_tl
+ ,bubble-bot-kern .initial:n = { .2ex }
+ ,bubble-bot-kern .value_required:n = true
+ ,bubble-side-kern .tl_set:N = \l_ducksay_bubble_side_kern_tl
+ ,bubble-side-kern .initial:n = { 0.2em }
+ ,bubble-side-kern .value_required:n = true
+ ,bubble-delim-top .tl_set:N = \l_ducksay_bubble_delim_top_tl
+ ,bubble-delim-left-1 .tl_set:N = \l_ducksay_bubble_delim_left_a_tl
+ ,bubble-delim-left-2 .tl_set:N = \l_ducksay_bubble_delim_left_b_tl
+ ,bubble-delim-left-3 .tl_set:N = \l_ducksay_bubble_delim_left_c_tl
+ ,bubble-delim-left-4 .tl_set:N = \l_ducksay_bubble_delim_left_d_tl
+ ,bubble-delim-right-1 .tl_set:N = \l_ducksay_bubble_delim_right_a_tl
+ ,bubble-delim-right-2 .tl_set:N = \l_ducksay_bubble_delim_right_b_tl
+ ,bubble-delim-right-3 .tl_set:N = \l_ducksay_bubble_delim_right_c_tl
+ ,bubble-delim-right-4 .tl_set:N = \l_ducksay_bubble_delim_right_d_tl
+ ,bubble-delim-top .initial:n = { { - } }
+ ,bubble-delim-left-1 .initial:n = (
+ ,bubble-delim-left-2 .initial:n = /
+ ,bubble-delim-left-3 .initial:n = |
+ ,bubble-delim-left-4 .initial:n = \c_backslash_str
+ ,bubble-delim-right-1 .initial:n = )
+ ,bubble-delim-right-2 .initial:n = \c_backslash_str
+ ,bubble-delim-right-3 .initial:n = |
+ ,bubble-delim-right-4 .initial:n = /
+ }
+% \end{macrocode}
+%
+%^^A<<<
+%
+% \subsubsection{Functions}^^A>>>
+%
+% \paragraph{Internal}^^A>>>
+%
+% \begin{macro}{\ducksay_evaluate_message_alignment_fixed_width_tabular:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_evaluate_message_alignment_fixed_width_tabular:
+ {
+ \tl_if_empty:NT \l_ducksay_msg_tabular_column_tl
+ {
+ \tl_set:Nx \l_ducksay_msg_tabular_column_tl
+ {
+ >
+ {
+ \str_case:Vn \l_ducksay_msg_align_tl
+ {
+ { l } { \exp_not:N \l_ducksay_msg_align_l_tl }
+ { c } { \exp_not:N \l_ducksay_msg_align_c_tl }
+ { r } { \exp_not:N \l_ducksay_msg_align_r_tl }
+ { j } { \exp_not:N \l_ducksay_msg_align_j_tl }
+ }
+ \exp_not:N \arraybackslash
+ }
+ p { \exp_not:N \l_ducksay_msg_width_dim }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_evaluate_message_alignment_fixed_width_vbox:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_evaluate_message_alignment_fixed_width_vbox:
+ {
+ \tl_set:Nx \l_ducksay_msg_align_vbox_tl
+ {
+ \str_case:Vn \l_ducksay_msg_align_tl
+ {
+ { l } { \exp_not:N \l_ducksay_msg_align_l_tl }
+ { c } { \exp_not:N \l_ducksay_msg_align_c_tl }
+ { r } { \exp_not:N \l_ducksay_msg_align_r_tl }
+ { j } { \exp_not:N \l_ducksay_msg_align_j_tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_calculate_msg_width_from_int:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_calculate_msg_width_from_int:
+ {
+ \hbox_set:Nn \l_ducksay_tmpa_box { \l_ducksay_msg_fount_tl M }
+ \dim_set:Nn \l_ducksay_msg_width_dim
+ { \l_ducksay_msg_width_int \box_wd:N \l_ducksay_tmpa_box }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_msg_tabular_begin:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_msg_tabular_begin:
+ {
+ \ducksay_msg_tabular_begin_inner:V \l_ducksay_msg_tabular_column_tl
+ }
+\cs_new:Npn \ducksay_msg_tabular_begin_inner:n #1
+ {
+ \begin { tabular } { @{} #1 @{} }
+ }
+\cs_generate_variant:Nn \ducksay_msg_tabular_begin_inner:n { V }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_msg_tabular_end:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_msg_tabular_end:
+ {
+ \end { tabular }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_digest_options:n}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_digest_options:n #1
+ {
+ \keys_set:nn { ducksay } { #1 }
+ \tl_if_empty:NT \l_ducksay_animal_tl
+ { \keys_set:nn { ducksay } { default_animal } }
+ \bool_if:NTF \l_ducksay_eat_arg_box_bool
+ {
+ \dim_compare:nNnTF { \l_ducksay_msg_width_dim } < { \c_zero_dim }
+ {
+ \int_compare:nNnTF { \l_ducksay_msg_width_int } < { \c_zero_int }
+ {
+ \cs_set_eq:NN
+ \ducksay_eat_argument:w \ducksay_eat_argument_hbox:w
+ }
+ {
+ \cs_set_eq:NN
+ \ducksay_eat_argument:w \ducksay_eat_argument_vbox:w
+ \ducksay_calculate_msg_width_from_int:
+ }
+ }
+ {
+ \cs_set_eq:NN \ducksay_eat_argument:w \ducksay_eat_argument_vbox:w
+ }
+ }
+ {
+ \dim_compare:nNnTF { \l_ducksay_msg_width_dim } < { \c_zero_dim }
+ {
+ \int_compare:nNnTF { \l_ducksay_msg_width_int } < { \c_zero_int }
+ {
+ \tl_if_empty:NT \l_ducksay_msg_tabular_column_tl
+ {
+ \str_case:Vn \l_ducksay_msg_align_tl
+ {
+ { l }
+ { \tl_set:Nn \l_ducksay_msg_tabular_column_tl { l } }
+ { c }
+ { \tl_set:Nn \l_ducksay_msg_tabular_column_tl { c } }
+ { r }
+ { \tl_set:Nn \l_ducksay_msg_tabular_column_tl { r } }
+ { j } {
+ \msg_error:nn { ducksay } { justify~unavailable }
+ \tl_set:Nn \l_ducksay_msg_tabular_column_tl { l }
+ }
+ }
+ }
+ }
+ {
+ \ducksay_calculate_msg_width_from_int:
+ \ducksay_evaluate_message_alignment_fixed_width_tabular:
+ }
+ }
+ {
+ \ducksay_evaluate_message_alignment_fixed_width_tabular:
+ }
+ \cs_set_eq:NN \ducksay_eat_argument:w \ducksay_eat_argument_tabular:w
+ }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_set_bubble_top_kern:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_set_bubble_top_kern:
+ {
+ \group_begin:
+ \l_ducksay_bubble_fount_tl
+ \exp_args:NNNx
+ \group_end:
+ \dim_set:Nn \l_ducksay_bubble_top_kern_dim
+ { \dim_eval:n { \l_ducksay_bubble_top_kern_tl } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_set_bubble_bottom_kern:}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_set_bubble_bottom_kern:
+ {
+ \group_begin:
+ \l_ducksay_bubble_fount_tl
+ \exp_args:NNNx
+ \group_end:
+ \dim_set:Nn \l_ducksay_bubble_bottom_kern_dim
+ { \dim_eval:n { \l_ducksay_bubble_bottom_kern_tl } }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_shipout:}^^A>>>
+% \begin{macrocode}
+\cs_new_protected:Npn \ducksay_shipout:
+ {
+ \hbox_set:Nn \l_ducksay_tmpa_box
+ { \l_ducksay_bubble_fount_tl \l_ducksay_bubble_delim_top_tl }
+ \int_set:Nn \l_ducksay_msg_width_int
+ {
+ \fp_eval:n
+ {
+ ceil
+ ( \box_wd:N \l_ducksay_msg_box / \box_wd:N \l_ducksay_tmpa_box )
+ }
+ }
+ \group_begin:
+ \l_ducksay_bubble_fount_tl
+ \exp_args:NNNx
+ \group_end:
+ \int_set:Nn \l_ducksay_msg_height_int
+ {
+ \int_max:nn
+ {
+ \fp_eval:n
+ {
+ ceil
+ (
+ (
+ \box_ht:N \l_ducksay_msg_box
+ + \box_dp:N \l_ducksay_msg_box
+ )
+ / ( \arraystretch * \baselineskip )
+ )
+ }
+ + \l_ducksay_vpad_int
+ }
+ { \l_ducksay_msg_height_int }
+ }
+ \hcoffin_set:Nn \l_ducksay_bubble_open_coffin
+ {
+ \l_ducksay_bubble_fount_tl
+ \begin{tabular}{@{}l@{}}
+ \int_compare:nNnTF { \l_ducksay_msg_height_int } = { \c_one_int }
+ {
+ \l_ducksay_bubble_delim_left_a_tl
+ }
+ {
+ \l_ducksay_bubble_delim_left_b_tl\\
+ \int_step_inline:nnn
+ { 3 } { \l_ducksay_msg_height_int }
+ {
+ \kern-\l_ducksay_bubble_side_kern_tl
+ \l_ducksay_bubble_delim_left_c_tl
+ \\
+ }
+ \l_ducksay_bubble_delim_left_d_tl
+ }
+ \end{tabular}
+ }
+ \hcoffin_set:Nn \l_ducksay_bubble_close_coffin
+ {
+ \l_ducksay_bubble_fount_tl
+ \begin{tabular}{@{}r@{}}
+ \int_compare:nNnTF { \l_ducksay_msg_height_int } = { \c_one_int }
+ {
+ \l_ducksay_bubble_delim_right_a_tl
+ }
+ {
+ \l_ducksay_bubble_delim_right_b_tl \\
+ \int_step_inline:nnn
+ { 3 } { \l_ducksay_msg_height_int }
+ {
+ \l_ducksay_bubble_delim_right_c_tl
+ \kern-\l_ducksay_bubble_side_kern_tl
+ \\
+ }
+ \l_ducksay_bubble_delim_right_d_tl
+ }
+ \end{tabular}
+ }
+ \hcoffin_set:Nn \l_ducksay_bubble_top_coffin
+ {
+ \l_ducksay_bubble_fount_tl
+ \int_step_inline:nn { \l_ducksay_msg_width_int + \l_ducksay_hpad_int }
+ { \l_ducksay_bubble_delim_top_tl }
+ }
+ \hcoffin_set:Nn \l_ducksay_msg_coffin { \box_use:N \l_ducksay_msg_box }
+ \bool_if:NF \l_ducksay_no_body_bool
+ {
+ \hcoffin_set:Nn \l_ducksay_body_coffin
+ {
+ \frenchspacing
+ \l_ducksay_body_fount_tl
+ \begin{tabular} { @{} l @{} }
+ \l_ducksay_animal_tl
+ \end{tabular}
+ }
+ \bool_if:NT \l_ducksay_mirrored_body_bool
+ {
+ \coffin_scale:Nnn \l_ducksay_body_coffin
+ { -\c_one_int } { \c_one_int }
+ \str_case:Vn \l_ducksay_body_to_msg_align_body_tl
+ {
+ { l } { \tl_set:Nn \l_ducksay_body_to_msg_align_body_tl { r } }
+ { r } { \tl_set:Nn \l_ducksay_body_to_msg_align_body_tl { l } }
+ }
+ }
+ }
+ \dim_set:Nn \l_ducksay_hpad_dim
+ {
+ (
+ \coffin_wd:N \l_ducksay_bubble_top_coffin
+ - \coffin_wd:N \l_ducksay_msg_coffin
+ ) / 2
+ }
+ \coffin_join:NnnNnnnn
+ \l_ducksay_msg_coffin { l } { vc }
+ \l_ducksay_bubble_open_coffin { r } { vc }
+ { - \l_ducksay_hpad_dim } { \c_zero_dim }
+ \coffin_join:NnnNnnnn
+ \l_ducksay_msg_coffin { r } { vc }
+ \l_ducksay_bubble_close_coffin { l } { vc }
+ { \l_ducksay_hpad_dim } { \c_zero_dim }
+ \ducksay_set_bubble_top_kern:
+ \ducksay_set_bubble_bottom_kern:
+ \coffin_join:NnnNnnnn
+ \l_ducksay_msg_coffin { hc } { t }
+ \l_ducksay_bubble_top_coffin { hc } { b }
+ { \c_zero_dim } { \l_ducksay_bubble_top_kern_dim }
+ \coffin_join:NnnNnnnn
+ \l_ducksay_msg_coffin { hc } { b }
+ \l_ducksay_bubble_top_coffin { hc } { t }
+ { \c_zero_dim } { \l_ducksay_bubble_bottom_kern_dim }
+ \bool_if:NF \l_ducksay_no_body_bool
+ {
+ \bool_if:NTF \l_ducksay_ignored_body_bool
+ { \coffin_attach:NVnNVnnn }
+ { \coffin_join:NVnNVnnn }
+ \l_ducksay_msg_coffin \l_ducksay_body_to_msg_align_msg_tl { b }
+ \l_ducksay_body_coffin \l_ducksay_body_to_msg_align_body_tl { t }
+ { \l_ducksay_body_x_offset_dim } { \l_ducksay_body_y_offset_dim }
+ }
+ \coffin_typeset:NVVnn \l_ducksay_msg_coffin
+ \l_ducksay_output_h_pole_tl \l_ducksay_output_v_pole_tl
+ { \l_ducksay_output_x_offset_dim } { \l_ducksay_output_y_offset_dim }
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% ^^A\subparagraph{Questionable Syntax Introducing Functions (Hacks)}^^A>>>
+%
+%^^A<<<
+%
+% \subparagraph{Message Reading Functions}^^A>>>
+%
+% Version 2 has different ways of reading the message argument of \cs{ducksay}
+% and \cs{duckthink}. They all should allow almost arbitrary content and the
+% height and width are set based on the dimensions.
+%
+% \begin{macro}{\ducksay_eat_argument_tabular:w}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_eat_argument_tabular:w
+ {
+ \bool_if:NTF \l_ducksay_eat_arg_tab_verb_bool
+ { \ducksay_eat_argument_tabular_verb:w }
+ { \ducksay_eat_argument_tabular_normal:w }
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_tabular_inner:w}^^A>>>
+% \begin{macrocode}
+\cs_new:Npn \ducksay_eat_argument_tabular_inner:w #1
+ {
+ \hbox_set:Nn \l_ducksay_msg_box
+ {
+ \l_ducksay_msg_fount_tl
+ \ducksay_msg_tabular_begin:
+ #1
+ \ducksay_msg_tabular_end:
+ }
+ \ducksay_shipout:
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_tabular_verb:w}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \ducksay_eat_argument_tabular_verb:w
+ { >{ \ducksay_process_verb_newline:nnn { ~ } { ~ \par } } +v }
+ { \ducksay_eat_argument_tabular_inner:w { \scantokens { #1 } } }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_tabular_normal:w}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \ducksay_eat_argument_tabular_normal:w { +m }
+ { \ducksay_eat_argument_tabular_inner:w { #1 } }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_hbox:w}^^A>>>
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ducksay_eat_argument_hbox:w
+ {
+ \afterassignment \ducksay_eat_argument_hbox_inner:w
+ \let \l_ducksay_nothing =
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_hbox_inner:w}^^A>>>
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ducksay_eat_argument_hbox_inner:w
+ {
+ \setbox \l_ducksay_msg_box \hbox \c_group_begin_token
+ \group_insert_after:N \ducksay_shipout:
+ \l_ducksay_msg_fount_tl
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_vbox:w}^^A>>>
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ducksay_eat_argument_vbox:w
+ {
+ \ducksay_evaluate_message_alignment_fixed_width_vbox:
+ \afterassignment \ducksay_eat_argument_vbox_inner:w
+ \let \l_ducksay_nothing =
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\ducksay_eat_argument_vbox_inner:w}^^A>>>
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ducksay_eat_argument_vbox_inner:w
+ {
+ \setbox \l_ducksay_msg_box \vbox \c_group_begin_token
+ \hsize \l_ducksay_msg_width_dim
+ \group_insert_after:N \ducksay_shipout:
+ \l_ducksay_msg_fount_tl
+ \l_ducksay_msg_align_vbox_tl
+ \@afterindentfalse
+ \@afterheading
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+%^^A<<<
+%
+% \subparagraph{Generating Variants of External Functions}^^A>>>
+%
+% \begin{macrocode}
+\cs_generate_variant:Nn \coffin_join:NnnNnnnn { NVnNVnnn }
+\cs_generate_variant:Nn \coffin_attach:NnnNnnnn { NVnNVnnn }
+\cs_generate_variant:Nn \coffin_typeset:Nnnnn { NVVnn }
+\cs_generate_variant:Nn \tl_if_eq:nnT { VnT }
+\cs_generate_variant:Nn \str_case:nn { Vn }
+\cs_generate_variant:Nn \regex_replace_all:NnN { Nnc }
+% \end{macrocode}
+%
+%^^A<<<
+%
+%^^A<<<
+%
+% \paragraph{Document level}^^A>>>
+%
+% \begin{macro}{\ducksay}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \ducksay { O{} }
+ {
+ \group_begin:
+ \tl_set:Nn \l_ducksay_say_or_think_tl { say }
+ \ducksay_digest_options:n { #1 }
+ \ducksay_eat_argument:w
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+% \begin{macro}{\duckthink}^^A>>>
+% \begin{macrocode}
+\NewDocumentCommand \duckthink { O{} }
+ {
+ \group_begin:
+ \tl_set:Nn \l_ducksay_say_or_think_tl { think }
+ \ducksay_digest_options:n { #1 }
+ \ducksay_eat_argument:w
+ }
+% \end{macrocode}
+% \end{macro}^^A<<<
+%
+%^^A<<<
+%
+%^^A<<<
+%
+% \begin{macrocode}
+%</code.v2>
+% \end{macrocode}^^A<<<
+%
+% \SetVersion{}%
+% \subsection{Definition of the Animals}^^A>>>
+%
+% \begin{macrocode}
+%<*animals>
+%^^A some of the below are from http://ascii.co.uk/art/kangaroo
+\AddAnimal{duck}%>>>
+{ \
+ \ __
+ >(' )
+ )/
+ /(
+ / `----/
+ \ ~=- /
+ ~^~^~^~^~^~^~^}%<<<
+\AddAnimal{small-duck}%>>>
+{ \
+ \
+ >()_
+ (__)__ _}%<<<
+\AddAnimal{duck-family}%>>>
+{ \
+ \ __
+ >(' )
+ )/
+ /(
+ / `----/ -()_ >()_
+ __\__~=-_/__ _(__)__(__)__ _}%<<<
+\AddAnimal{cow}%>>>
+{ \ ^__^
+ \ (oo)\_______
+ (__)\ )\/\
+ ||----w |
+ || ||}%<<<
+\AddAnimal{head-in}%>>>
+{ \
+ \ ^__^ /
+ (oo)\_______/ ________
+ (__)\ )=( ___|_ \____
+ ||----w | \ \ \____ |
+ || || || ||}%<<<
+\AddAnimal{sodomized}%>>>
+{ \ _
+ \ (_)
+ ^__^ / \
+ (oo)\_____/_\ \
+ (__)\ ) /
+ ||----w ((
+ || ||>>}%<<<
+\AddAnimal{tux}%>>>
+{ \
+ \ .--.
+ |o_o |
+ |\_/ |
+ // \ \
+ (| | )
+ /'\_ _/`\
+ \___)=(___/}%<<<
+\AddAnimal{pig}%>>>
++ \ _//| .-~~~-.
+ \ _/oo } }-@
+ ('')_ } |
+ `--'| { }--{ }
+ //_/ /_/+%<<<
+\AddAnimal{frog}%>>>
+{ \
+ \ (.)_(.)
+ _ ( _ ) _
+ / \/`-----'\/ \
+ __\ ( ( ) ) /__
+ ) /\ \._./ /\ (
+ )_/ /|\ /|\ \_(}%<<<
+\AddAnimal{snowman}%>>>
+{ \
+ \_[_]_
+ (")
+ >-( : )-<
+ (__:__)}%<<<
+\AddAnimal{hedgehog}%>>>
+{ \ .\|//||\||.
+ \ |/\/||/|//|/|
+ /. `|/\\|/||/||
+ o__,_|//|/||\||'}%<<<
+\AddAnimal{kangaroo}%>>>
+{ \
+ \ _,' ___
+ <__\__/ \
+ \_ / _\
+ \,\ / \\
+ // \\
+ ,/' `\_,}%<<<
+%^^A http://chris.com/ascii/index.php?art=animals/rabbits
+\AddAnimal{rabbit}%>>>
+{ \ / \`\ __
+ \ | \ `\ /`/ \
+ \ \_/`\ \-"-/` /\ \
+ | | \ |
+ (d b) \_/
+ / \
+ ,".|.'.\_/.'.|.",
+ / /\' _|_ '/\ \
+ | / '-`"`-' \ |
+ | | | |
+ | \ \ / / |
+ \ \ \ / / /
+ `"`\ : /'"`
+ `""`""`}%<<<
+\AddAnimal{bunny}%>>>
+{ \
+ \ /
+ /\ /
+ ( )
+ .( o ).}%<<<
+\AddAnimal{small-rabbit}%>>>
+{ \
+ \ _//
+ (')---.
+ _/-_( )o}%<<<
+\AddAnimal{dragon}%>>>
+{ \ / \ //\
+ \ |\___/| / \// \\
+ \ /0 0 \__ / // | \ \
+ / / \/_/ // | \ \
+ @_^_@'/ \/_ // | \ \
+ //_^_/ \/_ // | \ \
+ ( //) | \/// | \ \
+ ( / /) _|_ / ) // | \ _\
+ ( // /) '/,_ _ _/ ( ; -. | _ _\.-~ .-~~~^-.
+ (( / / )) ,-{ _ `-.|.-~-. .~ `.
+ (( // / )) '/\ / ~-. _ .-~ .-~^-. \
+ (( /// )) `. { } / \ \
+ (( / )) .----~-.\ \-' .~ \ `. \^-.
+ ///.----..> \ _ -~ `. ^-` ^-_
+ ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~
+ /.-~}%<<<
+%^^A http://www.ascii-art.de/ascii/def/dogs.txt
+\AddAnimal{dog}%>>>
+{ \ __
+ \ .-'\/\
+ "\ '------.
+ ___/ ( .'_____
+ '-----'"""'------"""""'}%<<<
+%^^A http://ascii.co.uk/art/squirrel
+\AddAnimal{squirrel}%>>>
+{ \ ,;:;;,
+ \ , ;;;;;
+ .=', ;:;;:,
+ /_', "=. ';:;:;
+ @=:__, \,;:;:'
+ _(\.= ;:;;'
+ `"_( _/="`
+ `"'``}%<<<
+\AddAnimal{snail}%>>>
+{ \
+ \ .-""-.
+ oo ; .-. :
+ \\__..-: '.__.')._
+ "-._.._'.__.-'_.."}%<<<
+%^^A http://www.ascii-art.de/ascii/uvw/unicorn.txt
+\AddAnimal{unicorn}%>>>
+{ \
+ \ /((((((\\\\
+ ---====((((((((((\\\\\
+ (( \\\\\\\
+ ( (* _/ \\\\\\\
+ \ / \ \\\\\\_ __,,__
+ | | | </ "------"" ((\\\\
+ o_| / / \ \\\\ \\\\\\\
+ | ._ ( \ \\\\\\\\\\\\\\\\
+ | / / / \\\\\\\ \\
+ .______/\/ / / / \\\
+ / __.____/ _/ ___----( /\
+ / / / ________/:______,---' \ / \_
+ / / \ \ \ \ \_ \
+ ( < \ \ > / \ \
+ \/ \\_ / / > )
+ \_| / / / /
+ _// _//
+ /_| /_|}%<<<
+%^^A https://asciiart.website//index.php?art=animals/other%20(water)
+\AddAnimal{whale}%>>>
+{ \ |-.
+ \ .-""-._ \ \.--|
+ \ / `-..__) ,-'
+ | . /
+ \--.__, .__.,'
+ `-.___'._\_.'}%<<<
+%^^A from http://www.ascii-art.de/ascii/s/starwars.txt :
+\AddAnimal{yoda}%>>>
+{ \
+ \ ____
+ \ _.' : `._
+ .-.'`. ; .'`.-.
+ __ / : ___\ ; /___ ; \ __
+ ,'_ ""--.:__;".-.";: :".-.":__;.--"" _`,
+ :' `.t""--.. '<@.`;_ ',@>` ..--""j.' `;
+ `:-.._J '-.-'L__ `-- ' L_..-;'
+ "-.__ ; .-" "-. : __.-"
+ L ' /.------.\ ' J
+ "-. "--" .-"
+ __.l"-:_JL_;-";.__
+ .-j/'.; ;"""" / .'\"-.
+ .' /:`. : : /.".''; `.
+ .-" / ;`.". : ."." : "-.
+ .+"-. : : ".".". ."." ;-._ \
+ ; \ `.; ; . "."-"." : : "+. ;
+ : ; ; ; . ."." ; : ; : \:
+ ; : ; : / / / , ;: ; :
+: \ ; : ; ; / : , : ; / ::
+; ; : ; : ; ; ; ; : ;:
+: : ; : ;. ; ' : : ; : ;
+;\ : ; : . , ; ; ; ;
+: `."-; : ; . ; : ; / ;
+ ; -: ; : , , ; : .-" :
+ :\ \ : ; , : \.-" :
+ ;`. \ ; : . , ;.'_..-- / ;
+ : "-. "-: ; , :/." .' :
+ \ \ : : ;/ __ :
+ \ .-`.\ /t-"" ":-+. :
+ `. .-" `l __/ /`. : ; ; \ ;
+ \ .-" .-"-.-" .' .'j \ / ;/
+ \ / .-" /. .'.' ;_:' ;
+ :-""-.`./-.' / `.___.'
+ \ `t ._ /
+ "-.t-._:'}%<<<
+\AddAnimal{yoda-head}%>>>
+{ \
+ \ ____
+ \ _.' : `._
+ .-.'`. ; .'`.-.
+ __ / : ___\ ; /___ ; \ __
+ ,'_ ""--.:__;".-.";: :".-.":__;.--"" _`,
+ :' `.t""--.. '<@.`;_ ',@>` ..--""j.' `;
+ `:-.._J '-.-'L__ `-- ' L_..-;'
+ "-.__ ; .-" "-. : __.-"
+ L ' /.------.\ ' J
+ "-. "--" .-"
+ __.l"-:_JL_;-";.__
+ .-j/'.; ;"""" / .'\"-.
+ .' /:`. : : /.".''; `.
+ .-" / ;`.". : ."." : "-.
+ .+"-. : : ".".". ."." ;-._ \}%<<<
+%^^A from https://www.ascii-code.com/ascii-art/movies/star-wars.php
+\AddAnimal{small-yoda}%>>>
+{ \
+ \
+ __.-._
+ '-._"7'
+ /'.-c
+ | /T
+ _)_/LI}%<<<
+\AddAnimal{r2d2}%>>>
+{ \
+ \ ,-----.
+ ,'_/_|_\_`.
+ /<<::8[O]::>\
+ _|-----------|_
+ | | ====-=- | |
+ | | -=-==== | |
+ \ | ::::|()|| /
+ | | ....|()|| |
+ | |_________| |
+ | |\_______/| |
+ / \ / \ / \
+ `---' `---' `---'}%<<<
+\AddAnimal{vader}%>>>
+{ \ _.-'~~~~~~`-._
+ \ / || \
+ / || \
+ | || |
+ | _______||_______ |
+ |/ ----- \/ ----- \|
+ / ( ) ( ) \
+ / \ ----- () ----- / \
+ / \ /||\ / \
+ / \ /||||\ / \
+ / \ /||||||\ / \
+/_ \O========O/ _\
+ `--...__|`-._ _.-'|__...--'
+ | `' |}%<<<
+%</animals>
+% \end{macrocode}^^A<<<
+%
+%^^A<<<
+%
+% \end{implementation}^^A<<<
+%
+% \closingpage
+%
+\endinput
+%
+^^A vim: ft=tex fdm=marker fmr=>>>,<<<