%% This package is distributed under the terms of the GPLv3 or later, or the %% LPPL 1.3c or later, which ever license fits your needs the best. %% %% Copyright (C) 2017 by Jonathan P. Spratte \NeedsTeXFormat{LaTeX2e} \RequirePackage{xparse,l3keys2e} \def\ducksay@version{v1.1} \def\ducksay@date{2017/09/25} \ProvidesExplPackage {ducksay} {\ducksay@date} {\ducksay@version} {cowsay for LaTeX} \ExplSyntaxOn \int_new:N \l_ducksay_strlen_int \int_new:N \l_ducksay_lines_int \seq_new:N \l_ducksay_msg_lines_seq \cs_new:Npn \ducksay_longest_line:n #1 {%>>> \int_incr:N \l_ducksay_lines_int \exp_args:NNx \tl_set:Nn \l_tmpa_tl { #1 } \regex_replace_all:nnN { \s } { \c{space} } \l_tmpa_tl \int_set:Nn \l_ducksay_strlen_int { \int_max:nn { \l_ducksay_strlen_int } { \tl_count:N \l_tmpa_tl } } }%<<< \cs_new:Npn \ducksay_open_bubble: {%>>> \begin{tabular}{@{}l@{}} \mbox{}\\ \int_compare:nNnTF {\l_ducksay_lines_int} = {1} { ( }{ / \int_step_inline:nnnn {3}{1} {\l_ducksay_lines_int} {\\\kern-0.5ex|} \\\detokenize{\ } } \\[-1ex]\mbox{} \end{tabular} \begin{tabular}{@{}l@{}} _\\ \int_step_inline:nnnn{2}{1}{\l_ducksay_lines_int}{\\}\\[-1ex] {-} \end{tabular} }%<<< \cs_new:Npn \ducksay_close_bubble: {%>>> \begin{tabular}{@{}l@{}} _\\ \int_step_inline:nnnn{2}{1}{\l_ducksay_lines_int}{\\}\\[-1ex] {-} \end{tabular} \begin{tabular}{@{}r@{}} \mbox{}\\ \int_compare:nNnTF {\l_ducksay_lines_int} = {1} { ) }{ \detokenize{\ } \int_step_inline:nnnn {3}{1}{\l_ducksay_lines_int} {\\|\kern-0.5ex} \\/ } \\[-1ex]\mbox{} \end{tabular} }%<<< \cs_new:Npn \ducksay_print_msg:n #1 #2 {%>>> \begin{tabular}{@{} #2 @{}} \int_step_inline:nnnn{1}{1}{\l_ducksay_strlen_int}{_}\\ #1\\[-1ex] \int_step_inline:nnnn{1}{1}{\l_ducksay_strlen_int}{{-}} \end{tabular} }%<<< \cs_new:Npn \ducksay_print:n #1 {%>>> \int_case:nnF { \l_ducksay_strlen_int } { { 0 } { \seq_set_split:Nnn \l_ducksay_msg_lines_seq {\\} { #1 } \seq_map_function:NN \l_ducksay_msg_lines_seq \ducksay_longest_line:n }} { \int_case:nn { \l_ducksay_lines_int } { { 0 } { \regex_count:nnN {\c{\\}} {#1} \l_ducksay_lines_int \int_incr:N \l_ducksay_lines_int }} } \frenchspacing \group_begin: \ttfamily \begin{tabular}[\l_ducksay_align_tl]{@{}l@{}} \ducksay_bubble: \begin{tabular}{@{}l@{}} \ducksay_open_bubble: \exp_args:Nno \ducksay_print_msg:n {#1} { \l_ducksay_msg_align_tl } \ducksay_close_bubble: \end{tabular}\\ \ducksay_body: \begin{tabular}{@{}l@{}} \l_ducksay_animal_tl \end{tabular} \end{tabular} \group_end: }%<<< \NewDocumentCommand{\ducksay}{ O{} m }{%>>> \group_begin: \int_zero:N \l_ducksay_strlen_int \int_zero:N \l_ducksay_lines_int \keys_set:nn { ducksay } { default_animal,#1 } \ducksay_print:n { #2 } \group_end: }%<<< % regexes for \duckthink >>> \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*)\\ } %<<< \NewDocumentCommand{\duckthink}{ O{} m }{%>>> \group_begin: \int_zero:N \l_ducksay_strlen_int \int_zero:N \l_ducksay_lines_int \keys_set:nn { ducksay } { default_animal,#1 } \regex_replace_once:NnN \c_ducksay_first_regex { \1O } \l_ducksay_animal_tl \regex_replace_once:NnN \c_ducksay_second_regex { \1o } \l_ducksay_animal_tl \regex_replace_once:NnN \c_ducksay_third_regex { \1o } \l_ducksay_animal_tl \ducksay_print:n { #2 } \group_end: }%<<< \keys_define:nn { ducksay } {%>>> ,bubble .code:n = \cs_set:Nn \ducksay_bubble: {#1} ,bubble .initial:n = \relax ,body .code:n = \cs_set:Nn \ducksay_body: {#1} ,body .initial:n = \relax ,align .tl_set:N = \l_ducksay_align_tl ,align .initial:n = {} ,align .value_required:n = true ,wd .int_set:N = \l_ducksay_strlen_int ,wd .value_required:n = true ,ht .int_set:N = \l_ducksay_lines_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 }%<<< \NewDocumentCommand{\DefaultAnimal}{ m }{%>>> \keys_define:nn { ducksay } { default_animal .meta:n = {#1} }}%<<< \NewDocumentCommand{\AddAnimal}{ s m +v }{%>>> \tl_set:Nn \l_tmpa_tl { \ #3 } \regex_replace_all:nnN { \r } { \c{tabularnewline}\c{null} } \l_tmpa_tl \cs_gset_eq:cN { g_ducksay_animal_#2_tl } \l_tmpa_tl \keys_define:nn { ducksay } { #2 .code:n = \cs_set_eq:Nc \l_ducksay_animal_tl { g_ducksay_animal_#2_tl } } \IfBooleanT{#1}{ \keys_define:nn { ducksay } { default_animal .meta:n = {#2} }} }%<<< \ExplSyntaxOff % Animals:>>> % 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}%>>> { \ \ _,' ___ <__\__/ \ \_ / _\ \,\ / \\ // \\ ,/' `\_,}%<<< \AddAnimal{rabbit}%>>> http://chris.com/ascii/index.php?art=animals/rabbits { \ / \`\ __ \ | \ `\ /`/ \ \ \_/`\ \-"-/` /\ \ | | \ | (d b) \_/ / \ ,".|.'.\_/.'.|.", / /\' _|_ '/\ \ | / '-`"`-' \ | | | | | | \ \ / / | \ \ \ / / / `"`\ : /'"` `""`""`}%<<< \AddAnimal{bunny}%>>> { \ \ \ \ /\ ( ) .( o ).}%<<< \AddAnimal{small-rabbit}%>>> { \ \ _// (')---. _/-_( )o}%<<< \AddAnimal{dragon}%>>> { \ / \ //\ \ |\___/| / \// \\ \ /0 0 \__ / // | \ \ / / \/_/ // | \ \ @_^_@'/ \/_ // | \ \ //_^_/ \/_ // | \ \ ( //) | \/// | \ \ ( / /) _|_ / ) // | \ _\ ( // /) '/,_ _ _/ ( ; -. | _ _\.-~ .-~~~^-. (( / / )) ,-{ _ `-.|.-~-. .~ `. (( // / )) '/\ / ~-. _ .-~ .-~^-. \ (( /// )) `. { } / \ \ (( / )) .----~-.\ \-' .~ \ `. \^-. ///.----..> \ _ -~ `. ^-` ^-_ ///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~ /.-~}%<<< \AddAnimal{dog}%>>> http://www.ascii-art.de/ascii/def/dogs.txt { \ __ \ .-'\/\ "\ '------. ___/ ( .'_____ '-----'"""'------"""""'}%<<< \AddAnimal{squirrel}%>>> http://ascii.co.uk/art/squirrel { \ ,;:;;, \ ;;;;; .=', ;:;;:, /_', "=. ';:;:; @=:__, \,;:;:' _(\.= ;:;;' `"_( _/="` `"'``}%<<< % 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/'.; ;"""" / .'\"-. .' /:`. : : /.".''; `. .-" / ;`.". : ."." : "-. .+"-. : : ".".". ."." ;-._ \}%<<< % 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/ _\ `--...__|`-._ _.-'|__...--' | `' |}%<<< %<<< \ExplSyntaxOn \ProcessKeysOptions { ducksay } \keys_define:nn { ducksay } { ,animal .undefine: } \ExplSyntaxOff \endinput % vim: fdm=marker foldmarker=>>>,<<<