# Expandable author names from biblatex?

Knowing that \printnames command in biblatex is not expandable as it is protected, and I cannot otherwise do much about it; I thought about going through biblatex.sty and reconstructing \printnames with unprotected commands - in hope that I can get an expandable version.

Note that while \printfield{FIELD} is not expandable, \thefield{FIELD} is (and it simply calls commands of the type \abx@field@FIELD) - so I don't have that much problem with "plain" fields. However, for names, one needs to declare a name format, and then one needs to iterate through a list (which ultimately uses \abx@name@FIELD), so it's more complicated.

This is MWE of how far I got - still not working:

\documentclass{article}

\usepackage{trace}

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
author = {Author1 Authorsen1 and Author1a Authorsen1a},
howpublished = {12-23-2001},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
author = {Author2},
howpublished = {12-23-2002},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title XX3},
author = {Author2 Authorsen2 and Author2a Authorsen2a},
howpublished = {12-23-2003},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
author = {Author4},
howpublished = {12-23-2004},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}

\usepackage[bibstyle=numeric,citestyle=numeric]{biblatex}
\ExecuteBibliographyOptions{%
abbreviate=false,%
uniquename=allfull,%
maxnames=99,%
minnames=99,%
backend=biber,%
}

\DeclareNameFormat{testauthors}{% now only this works
\iffirstinits
{\usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}
{\usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}%
\usebibmacro{name:andothers}
}%

% copy of printnames:
\makeatletter
\def\getAuthsX{
%   \let\@tempc\relax % no effect
%
\blx@getformat\blx@theformat{nfd}{testauthors}{author} %
\blx@begunit
\blx@namesetup{1}{99}{author}% {#2}{#3}{#4} - start, stop, 'namelist'
\expandafter\blx@nameparser\blx@thedata{}&%
\blx@endunit
}
\def\getAuths{\blx@listargs\getAuthsX}
\makeatother

% just a test for usual field:
\DeclareFieldFormat{myentrykey}{%
Testing #1 %
}

\newbibmacro*{myentrycite}{% custom
\printfield[myentrykey]{entrykey}
} % end \newbibmacro*{

\DeclareCiteCommand{\myentrycite}
{\usebibmacro{prenote}}
{\usebibmacro{citeindex}%
\printtext{\usebibmacro{myentrycite}}}
{}
{\usebibmacro{postnote}}

\begin{document}

\myentrycite{num03}

\getAuths

\end{document}


I would ultimately like to use something like \edef\mytemp{\getAuths}; but for now, I'm just testing if it will work in a typesetting context. Building this with:

pdflatex test.tex
biber test
pdflatex test.tex


... and the \getAuths function breaks here:

! Missing \endcsname inserted.
\@tempc
\KV@split ...efix \@tempa \endcsname \ifx \@tempc
\relax \KV@errx {\@tempa \...

\KV@do ...lax #1\empty \else \KV@split #1==\relax
\expandafter \KV@do \fi
\setkeys ...{KV@#1@}\let \@tempc \relax \KV@do #2,
\relax ,
\blx@nameparser@i ...{\setkeys {blx@opt@name}{#1}}
\blx@theformat
<argument> ...rt \else \blx@nameparser@i \ifblank

\@secondoftwo #1#2->#2

\getAuthsX ...dafter \blx@nameparser \blx@thedata
{}&\blx@endunit
\reserved@c ->\getAuthsX
{}{}{}
\par


As far as I can see, it complains about \@tempc - but even me setting \let\@tempc\relax explicitly before the run doesn't help (not to mention that, it should already be set to \relax in \setkeys, if I read that stack trace correctly)

If one insists with pressing ENTER for the run to complete, the output in the PDF of \getAuths is something like:

,1 and ∆ ’ ¿ and ’ ¿


.... which (seemingly) means the author names loop has been run - but obviously, not the right data is retrieved.

Could anyone confirm if this is possible to do - and if so, explain what am I missing to have it work?

Is it that \let is not expandable? But what does it matter here in this example then, as I'm not yet calling \edef ?!

EDIT:

Ok, the first problem was an oversight of mine - calling something like \thefield{FIELD} implies there is a "current" bib item being processed - and having \getAuths called from the body of the document does not set any specific bib item.

So the solution to that, is simply have \getAuths be called from a command that selects a specific bibliography entry, such as \myentrycite{num03} (I knew there was a reason I kept all that code there :)).

So with the following change in above example:

  ....
\DeclareFieldFormat{myentrykey}{%
Testing #1 \getAuthsX  %
\edef\tmpa{\getAuthsX}
}
...
\begin{document}

\myentrycite{num03}

\end{document}


... one can see the author names of entry num03 typeset in the PDF document, which was the starting intent (and one can just use the \getAuthsX directly).

However, I still cannot get the expansion (as in \edef\tmpa{\getAuthsX} to work), so I'd appreciate any pointers there...

-
\let is indeed not expandable, nor other macro definitions are... –  yo' Jun 20 '12 at 14:09

Expandable macros can be defined from unformatted bibliographic data. The \savename command simply copies an unformatted name list to a macro. These data can also be accessed via formatting directives defined with \DeclareNameFormat or \DeclareIndexNameFormat.

\DeclareIndexNameFormat is intended to write data to the index, but we can use it to pick up data from each name in the list. Inside its resulting directive, biber-only name delimiters (e.g. \bibinitperiod) expand to plain text. This is useful because biblatex punctuation commands are only defined within citation or bibliography commands.

The document below demonstrates a formatting directive getname that appends the first names (#3) (or first initials (#4) under the firstinits=true option setting), last name prefix (#5) and last name (#1) to the expandable macro \name. The directive is executed by the \getauthor citation command.

\documentclass{article}
\usepackage[american]{babel}
\usepackage[backend=biber,firstinits]{biblatex}

\newcommand*{\name}{}
\DeclareIndexNameFormat{getname}{%
\def\space{ }
\ifnumgreater{\value{listcount}}{\value{liststart}}
{\ifnumless{\value{listcount}}{\value{liststop}}
{\ifnumgreater{\value{liststop}}{2}
{\xappto{\name}{\finalandcomma}}
{}%
\xappto{\name}{\space\bibxstring{and}\space}}}
{}%
\ifblank{#4}
{}
{\iffirstinits
{\xappto{\name}{#4\space}}
{\xappto{\name}{#3\space}}}%
\ifblank{#5}
{\xappto{\name}{#1}}
{\xappto{\name}{#5\space#1}}}

\DeclareCiteCommand{\getauthor}
{\undef\name
\boolfalse{citetracker}%
\boolfalse{pagetracker}}
{\ifnameundef{author}
{}
{\ifboolexpr{ test {\ifnumgreater{\value{citecount}}{1}}
and not test {\ifundef{\name}} }
{\gappto{\name}{; }}
{}%
\indexnames[getname][1-99]{author}}}
{}
{}

\begin{document}
\getauthor{knuth:ct,vangennep}
\edef\temp{Some text \name}
\subsubsection*{\temp}
\getauthor{companion,aristotle:anima,ctan}
\subsubsection*{\MakeUppercase{\name}}
\textcite{cms,companion,aristotle:anima,ctan}
\end{document}

-
Thank you, @Audrey - that example looks great! I wish I knew about it before I tried wrestling with the problem myself :) Many thanks again - cheers! –  sdaau Jun 21 '12 at 3:42
@sdaau No problem. I liked that your solution was consistent with localization and name-related option settings. I've made an edit borrowing some of these ideas. –  Audrey Jun 21 '12 at 4:47

Well, shiver me timbers! Getting this done was a PAIN!!

@Audrey already answered an hour ago, however, I also got to a solution in the meantime, so I'd just like to document it here...

To begin with - the most important part for me - here is the terminal output of the (not so) MWE, posted at end:

(./test.aux) (./test.bbl)
taA__Authorsen1__A.\spacefactor \blx@sf@dot ______
taB__Authorsen1__Author1______
ta --Author1 Authorsen1==
taA__Authorsen1a__A.\spacefactor \blx@sf@dot ______
taB__Authorsen1a__Author1a______
ta -- and Author1a Authorsen1a.==

= currentauthors =
==Author1 Authorsen1 and Author1a Authorsen1a.==

taA__Authorson2__A.\spacefactor \blx@sf@dot ______
taB__Authorson2__Author2______
ta --Author2 Authorson2.==

= currentauthors =
==Author2 Authorson2.==

taA__Authorsen3__A.\spacefactor \blx@sf@dot ______
taB__Authorsen3__Author3______
ta --Author3 Authorsen3==
taA__Authorsen3A__A.\spacefactor \blx@sf@dot ______
taB__Authorsen3A__Author3A______
ta --, Author3A Authorsen3A==
taA__Authorsen3B__A.\spacefactor \blx@sf@dot ______
taB__Authorsen3B__Author3B______
ta --, and Author3B Authorsen3B.==

= currentauthors =
==Author3 Authorsen3, Author3A Authorsen3A, and Author3B Authorsen3B.==

Underfull \hbox (badness 10000) in paragraph at lines 407--408


Names seem to be properly expanded as 'plain text'; however, the example also shows that this approach can still coexist with the usual biblatex typesetting:

(the main intent of the expandable process is simply to provide text characters; however in the MWE it also typesets (though not succesfully) simply as an example - otherwise, only a single command should be commented to suppress the typeset output, see code)

## Discussion

A few words, while I can still remember... Basically, it was all spinning in circles and looking at endless traceon outputs, until one realizes that the \DeclareNameFormat is the main part where one could have those names at all; then by accident I tried to wrap an \edef over its inner macros:

%   \DeclareNameFormat{testauthors}{% now only this works
%   \typeout{ta==#1==#4==#5==#7==}
%   \typeout{ta==#1==#3==#5==#7==}
%   \edef\tmpa{
%   \iffirstinits
%     {\usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}
%     {\usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}%
%   \usebibmacro{name:andothers}
%   }
%   \typeout{ta--\tmpa==}
%   \tmpa
%   }%


... and while that failed - it also sparked the idea to wrap only the \usebibmacros in separate \edef (which is the basis of the current solution):

%   \DeclareNameFormat{testauthors}{% now only this works
%   \typeout{ta==#1==#4==#5==#7==}
%   \typeout{ta==#1==#3==#5==#7==}
%
%   \iffirstinits
%     {\edef\tmpa{\usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}}
%     {\edef\tmpa{\usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}}%
%   \usebibmacro{name:andothers}
%
%   \typeout{ta--\tmpa==}
%   \tmpa
%   }%


That didn't work very well either - but at least it started showing which commands are the unexpandable ones; so I could redefine them in my .tex file as unprotected commands. So again by a long process of traceing, I started copying existing functions from biblatex.sty into my .tex file. This part is again not trivial - because not all functions can be found defined verbatim in biblatex.sty; then one can use either \show in the .tex document itself - or texdef (usually when a definition is not shown with one method, it will be shown with one of the other two).

Then, one should keep in mind that a protected biblatex command like \usebibmacro{name:first-last} has an "internal" counterpart in \abx@macro@name:delim (and I still cannot tell how I figured out the proper relationships, apart from staring in logs and source files :)) But the internal commands could be protected as well - or if not, may make use of protected macros; so it takes a while to identify a "minimal" set of "hacked" definitions that are needed.

Another thing to keep in mind is that \DeclareNamesFormat will be called for each individual name in a citation; therefore, one cannot expect to "pull" a complete "names list" in one pass - in the MWE below, there is a "global" "variable" (\currentauthors), which is used to concatenate the contents generated in each pass - and should contain the complete author name list as expanded text, once the custom cite process is finished.

Also, I should note that wherever I could, I tried removing typesetting commands like \unhbox and such, as they are typically also non-expandable; there seems to be some calculation for interpunction in biblatex based on diverse skips - but their removal doesn't seem to have too much effect on the expanded names list.

I got to a critical point when I got this log output:

taA==Authorsen2==A.======
taB==Authorsen2==Author2======
ta --Author2 Authorsen2 ==
taA==Authorsen2a==A.======
taB==Authorsen2a==Author2a======
ta --\ifboolexpr {test {0}ortest 0}{, }{ and }Author2a Authorsen2a ==


... that meant that almost all I needed was expanded - except for a \ifboolexpr. First I thought of using boolexpr package, but that conflicts with etoolbox/biblatex ... Then it turns out that there is an expandable \ifboolexpe in etoolbox as well - and once that got replaced, I could in fact remove some superfluous redefinitions.

Once the whole thing started generating decent names (the rules biblatex uses to compose names lists seem to be preserved, also for the expanded text output), there was another problem: since I basically redefined "core" macros of biblatex, now I could have a custom cite which is expandable - but the "usual" non-expandable custom cite wouldn't work anymore!

That got solved by adding the prefix EX to all redefined macro names, to help distinguish them from the default ones - and that seems to allow for a mix of methods, that allow both usual typesetting of author names via biblatex; and also allow for expansion of the same.

Well, I guess that was all I could remember :) However, there are plenty of comments in the code - so hope it helps someone..

## MWE Code

(build w/ pdflatex test.tex ; biber test ; pdflatex test.tex)

\documentclass{book}

\usepackage{trace}

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
author = {Author1 Authorsen1 and Author1a Authorsen1a},
howpublished = {12-23-2001},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
author = {Author2 Authorson2},
howpublished = {12-23-2002},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title XX3},
author = {Author3 Authorsen3 and Author3A Authorsen3A and Author3B Authorsen3B},
howpublished = {12-23-2003},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
author = {Author4},
howpublished = {12-23-2004},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}

\usepackage[bibstyle=numeric,citestyle=numeric]{biblatex}
\ExecuteBibliographyOptions{%
abbreviate=false,%
uniquename=allfull,%
maxnames=99,%
minnames=99,%
backend=biber,%
}

%\usepackage{boolexpr} %tlmgr install boolexpr ; expandable? yes, but:
% overwrites the \ifboolexpr by etoolbox (loaded by biblatex),
% and then everything breaks.. so cannot use

\makeatletter

% must keep, else appears as unexpanded in output
% was \protected ; used \isdot
%
% (biid): dot in mathmode;
%         \mathchardef\blx@sf@dot=1002;
%         1002 abbreviation period (dot)
%         typesetting related ?! don't use, it doesn't expand
%         doesn't run in this example (we don't typeset anymore)?
%         but it can kick in, if \abx@dot gets added here after fi
%
\def\EXblx@imc@isdot{%
\ifnum\blx@spacefactor=\blx@sf@period
%\spacefactor%                    (rtc)
\blx@sf@dot %                     (biid)
\fi
}

% must keep, to remove unexpanded typesettin cmds
%
\def\EXabx@dot{%
\ifdim\lastkern>\z@\unkern\fi
.%
%\spacefactor%                      (rtc)
%\blx@sf@dot%                       (rtc)
}%

% must keep, to remove unexpanded \unspace
%
%\unspace%                          (rtc)
\ifnum\blx@spacefactor<\blx@sf@threshold@low
\csuse{blx@qp@#1}\csuse{abx@#1}%
\else
\ifnum\blx@spacefactor>\blx@sf@threshold@high
\csuse{blx@qp@#1}\csuse{abx@#1}%
\else
\ifcsdef{blx@pp@\the\csname blx@sf@#1\endcsname} %
{\csuse{blx@qp@#1}\csuse{abx@#1}}
{\csuse{blx@qp@#1}}%
\fi
\fi
\csuse{blx@pq@#1}}

% must keep, else appears as unexpanded in output
% was \protected ; used \adddot
%
\ifnum\blx@spacefactor=\blx@sf@period
%\spacefactor%                    (rtc)
%\blx@sf@dot%                     (rtc)
\fi}

% must keep, else "! Undefined control sequence."
% was \protected ; used \addcomma
%

% was \newrobustcmd* ; used \adddot
%

% must keep, else appears as unexpanded in output
% was \protected
%
\def\EXblx@imc@resetpunctfont{%
\blx@ifpuncthook
{\global\let\abx@puncthook\@firstofone}
{}}

% (rtc) removed typesetting cmd

% must keep, else appears as unexpanded in output
% was \newrobustcmd
%
%\unspace%                          (rtc)
\blx@postpunct%
%\penalty\value{lownamepenalty}%    (rtc)
\space%
\EXblx@imc@resetpunctfont}

% must keep, else appears as unexpanded in output
% was \newrobustcmd
%

% must keep, else appears as unexpanded in output
%  was \protected
%
%\unspace%    %                     (rtc)
\blx@postpunct%
\space%
\EXblx@imc@resetpunctfont%
}

% leaving \def\finalnamedelim out would cause trouble;
%  even if by default it if not protected..
%  that is because here, we want to avoid use of
%  protected \bibstring => instead, \blx@imc@bibxstring
%
\def\EXfinalnamedelim{% not protected - for ref..
\ifnumgreater{\value{liststop}}{2}%
{\finalandcomma}%
{}%
\blx@imc@bibxstring{and}%\bibstring{and}%
\space%
}

\def\EXlbx@finalnamedelim#1{%
\EXfinalnamedelim%
}

% redef without long -- now namebreak no complain:
%
% if this \def for namebreak is commented,
% then starting to get in console:
% "### semi simple group (level 3) entered at line 333 (\begingroup)"
%
% but must now re-define it along w/ nameparser, to allow
% interface with normal work
%
\def\EXblx@namebreak#1&{}

\def\EXblx@nameparser#1{%
\ifblank{#1}
{\EXblx@namebreak\relax}
{\ifnum\c@listcount<\c@liststart
\else
\EXblx@nameparser@i#1%
\fi
\ifnum\c@listcount>\c@liststop
\expandafter\EXblx@namebreak
\fi
\EXblx@nameparser}}

\def\EXblx@nameparser@i#1{%
\ifblank{#1}
{}
{\setkeys{blx@opt@name}{#1}}% this
\blx@theformat}

% also had previously redefined here:
%   \DeclareRobustCommand{\KV@prefix}{}
%   \def\blx@namesetup@i#1{%
%   \def\blx@namesetup@ii#1#2{%
%   \def\blx@namecodes{%
%   \def\blx@namesetup#1#2#3{%

% originally, here \ifboolexpr was used;
%  when used in expandable context; \ifboolexpr will fail with
%  "! Argument of \@secondoftwo has an extra }" - UNLESS
%  default paths (like {True}{False} for \ifnumless;
%  and \ifmorenames must have one char only, {1}{0}) are added!
%  only then, not getting the \@secondoftwo problem!
% BUT: \ifboolexpr robust = \ifboolexpe expandable
% if using \ifboolexpe - then should NOT have the added T/F branches!
%
\expandafter\def\csname EXabx@macro@name:delim\endcsname#1{%
\ifnumgreater{\value{listcount}}{\value{liststart}}%
{\ifboolexpe{%
test {\ifnumless{\value{listcount}}{\value{liststop}}}%
or%
test {\ifmorenames}%% \blx@imc@ifmorenames
}%
{\multinamedelim}{\EXlbx@finalnamedelim{#1}}%
}%
{}%
%\show\bibstring%\typeout{==name:delim==#1==}% bibstring=\protected macro:
}

% (nfl1): MUST have comment after cmd here; else a spurious space
%         appears at end of last name! also removed \blx@imc@isdot (typeset)
% (nfl2): added: hack to "hardcode" a dot after all of the names are done
% (nfl3): \ifmorenames -> \blx@imc@ifmorenames
%
\expandafter\def\csname EXabx@macro@name:first-last\endcsname#1#2#3#4{%
\csname EXabx@macro@name:delim\endcsname{#2#3#1}%
\csname abx@macro@name:hook\endcsname{#2#3#1}%
\ifblank{#2}%
{}%
{\mkbibnamefirst{#2}\EXblx@imc@isdot\EXbibnamedelimd}%
\ifblank{#3}%
{}%
{\mkbibnameprefix{#3}\EXblx@imc@isdot%
\ifpunctmark{'}{}{%
\ifuseprefix{\bibnamedelimc}{\EXbibnamedelimd}}%
}%
\mkbibnamelast{#1}\EXblx@imc@isdot%
\ifblank{#4}{}%
{\EXbibnamedelimd\mkbibnameaffix{#4}}%  (nfl1)
\ifboolexpe{%                         (nfl2)
test {\ifnumless{\value{listcount}}{\value{liststop}}}%
or%
test {\ifmorenames}%              (nfl3)
}%
{}{\EXabx@dot}%
}

% (dnf1): expanding name:andothers is currently a problem;
%          fails w/ "! Argument of \@secondoftwo has an extra }."
%          but, 'and' in names gets added automatically - without name:andothers;
%          name:andothers just if we want it shortened ("and others").
%          and I don't need that currently..
% (dnf2): perform concatenation here;
% (dnf3): this "return" output of \tmpa also typesets;
%          comment it out if typeset not desired
%
\DeclareNameFormat{testauthors}{%
\typeout{taA__#1__#4__#5__#7__}
\typeout{taB__#1__#3__#5__#7__}
%\traceon
\iffirstinits%
{\edef\tmpa{\csname EXabx@macro@name:first-last\endcsname{#1}{#4}{#5}{#7}}}%
{\edef\tmpa{\csname EXabx@macro@name:first-last\endcsname{#1}{#3}{#5}{#7}}}%
%\edef\tmpb{\csname abx@macro@name:andothers\endcsname } % (dnf1)
\csname abx@macro@name:andothers\endcsname

\typeout{ta --\tmpa==}
\global\edef\currentauthors{\currentauthors\tmpa} % (dnf2)
\tmpa %                                             (dnf3)
}%

%\blx@namesetup: {#2}{#3}{#4} - start, stop, 'namelist';
%            eg. {1}{99}{author} - but can do without
%
\def\getAuthsX{%
\blx@getformat\blx@theformat{nfd}{testauthors}{author}%
\blx@namesetup{}{}{author}%
\expandafter\EXblx@nameparser\blx@thedata{}&%
}

\makeatother

\def\getAuths{%
\getAuthsX%
}

\newbibmacro*{myentrycite}{% custom
\global\def\currentauthors{} % init empty string for concat
\getAuths
}

\DeclareCiteCommand{\myentrycite}
{}
{\usebibmacro{myentrycite}}
{}
{}

%%%%%%%%%%%%

\DeclareNameFormat{normauthors}{%
\iffirstinits
{\usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}
{\usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}%
\usebibmacro{name:andothers}
}%

\newbibmacro*{normentrycite}{% custom
\printnames[normauthors]{author}%
}

\DeclareCiteCommand{\normentrycite}
{}
{\usebibmacro{normentrycite}}
{}
{}

\begin{document}

%\traceon

\textit{Author names output of expandable cite commands:}

% adding new cites will require a new biber run!
\myentrycite{num01}
\typeout{^^J= currentauthors =^^J==\currentauthors==^^J}

\myentrycite{num02}
\typeout{^^J= currentauthors =^^J==\currentauthors==^^J}

\myentrycite{num03}
\typeout{^^J= currentauthors =^^J==\currentauthors==^^J}

\bigskip

\textit{Author names output of usual cite command:} \\

% will give "Runaway argument?" if all
% defaults are reprogrammed; and not
% handled with separate names!
\normentrycite{num03}

\end{document}

-