diff options
author | Karl Berry <karl@freefriends.org> | 2018-09-21 21:06:08 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2018-09-21 21:06:08 +0000 |
commit | 0972697fc4429b971f75335c69d8a71b9999097e (patch) | |
tree | 6a2c508754362eee8c4e9be2f255da6f58d67529 /Master/texmf-dist/source/latex/ducksay | |
parent | c240d190fee85a0d263ff59264eaf1d7c5cdbdab (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.dtx | 2291 |
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=>>>,<<< |