%% 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.2} \def\ducksay@date{2017/10/30} \ProvidesExplPackage {ducksay} {\ducksay@date} {\ducksay@version} {cowsay for LaTeX} \ExplSyntaxOn % variables>>> \int_new:N \l_ducksay_strlen_int \int_new:N \l_ducksay_lines_int \seq_new:N \l_ducksay_msg_lines_seq \tl_new:N \l_ducksay_align_tl \tl_new:N \l_ducksay_msg_align_tl \tl_new:N \l_ducksay_animal_tl \bool_new:N \l_ducksay_empty_ligatures_bool \cs_new:Nn \ducksay_bubble: {} \cs_new:Nn \ducksay_body: {} \regex_new:N \l_ducksay_ligatures_regex %<<< % predefined regexes>>> % 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*)\\ } %<<< % regexes for \AddAnimal >>> \regex_const:Nn \c_ducksay_newline_regex { \r } \regex_set:Nn \l_ducksay_ligatures_regex { [\-<>`] } %<<< %<<< \keys_define:nn { ducksay } {%>>> ,bubble .code:n = \cs_set:Nn \ducksay_bubble: {#1} ,body .code:n = \cs_set:Nn \ducksay_body: {#1} ,align .tl_set:N = \l_ducksay_align_tl ,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 ,ligatures .code:n = { \tl_if_empty:nTF { #1 } { \bool_set_true:N \l_ducksay_empty_ligatures_bool } { \regex_set:Nn \l_ducksay_ligatures_regex { #1 } } } }%<<< \ProcessKeysOptions { ducksay } \cs_new:Nn \ducksay_longest_line:n {%>>> \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:Nn \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] \mbox{-} \end{tabular} }%<<< \cs_new:Nn \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:Nn \ducksay_print_msg:nn {%>>> \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_generate_variant:Nn \ducksay_print_msg:nn { nV } \cs_new:Nn \ducksay_print:n {%>>> \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 }} } \group_begin: \frenchspacing \ttfamily \verbatim@font \@noligs \begin{tabular}[\l_ducksay_align_tl]{@{}l@{}} \ducksay_bubble: \begin{tabular}{@{}l@{}} \ducksay_open_bubble: \ducksay_print_msg:nV {#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: }%<<< \cs_new:Nn \ducksay_prepare_say_and_think:n {%>>> \int_zero:N \l_ducksay_strlen_int \int_zero:N \l_ducksay_lines_int \keys_define:nn { ducksay } { animal .meta:n = { ##1 } } \keys_set:nn { ducksay } { default_animal,#1 } }%<<< \cs_new:Nn \ducksay_to_duckthink: {%>>> \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 }%<<< \NewDocumentCommand{\ducksay}{ O{} m }{%>>> \group_begin: \ducksay_prepare_say_and_think:n { #1 } \ducksay_print:n { #2 } \group_end: }%<<< \NewDocumentCommand{\duckthink}{ O{} m }{%>>> \group_begin: \ducksay_prepare_say_and_think:n { #1 } \ducksay_to_duckthink: \ducksay_print:n { #2 } \group_end: }%<<< \NewDocumentCommand{\DefaultAnimal}{ m }{%>>> \keys_define:nn { ducksay } { default_animal .meta:n = {#1} }}%<<< \NewDocumentCommand{\DucksayOptions}{ m }{%>>> \keys_set:nn { ducksay } { #1 }}%<<< \NewDocumentCommand{\AddAnimal}{ s m +v }{%>>> \tl_set:Nn \l_tmpa_tl { \ #3 } \bool_if:NF \l_ducksay_empty_ligatures_bool { \regex_replace_all:NnN \l_ducksay_ligatures_regex { \c{mbox}\0 } \l_tmpa_tl } \regex_replace_all:NnN \c_ducksay_newline_regex { \c{tabularnewline}\c{null} } \l_tmpa_tl \tl_gset_eq:cN { g_ducksay_animal_#2_tl } \l_tmpa_tl \keys_define:nn { ducksay } { #2 .code:n = \tl_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/uvw/unicorn.txt \AddAnimal{unicorn} { \ \ /((((((\\\\ ---====((((((((((\\\\\ (( \\\\\\\ ( (* _/ \\\\\\\ \ / \ \\\\\\_ __,,__ | | | / \ \ \/ \\_ / / > ) \_| / / / / _// _// /_| /_|} % 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/ _\ `--...__|`-._ _.-'|__...--' | `' |}%<<< %<<< \endinput % vim: fdm=marker foldmarker=>>>,<<<