4

With biblatex, it’s quite simple to choose how author (or editor) names and lists thereof are displayed in the bibliography. One can choose the order of given and family names (and other name parts), decide whether given and middle names should be abbreviated as initials or left out completely, and set a maximum of people to appear, the rest being hidden in “et al.” or a local variant thereof.

How can I get biblatex to automatically use the longest form that fits into a single line (or into an arbitrary width)?

I’d like it to try the following variants (where n is the number of names):

  1. All names, all full.
  2. All names, initials for the i right-most ones (1 ≤ in).
  3. All names with initials, remove the i right-most ones (1 ≤ in–1), add “et al.” instead.

Illustrative code:

\documentclass[12pt,a4paper]{minimal}
\usepackage[showframe]{geometry}
\newcommand*\yearandtitle{\hfill \quad 2012\\
\textit{A very interesting book. With subtitle}. \hfill Someplace / Otherplace: SUP Press\par\medskip}
\begin{document}
% full line
\noindent Agnes Ursula \textsc{Thor}, Charles-Oliver \textsc{Author}, William \textsc{Riter} and Claire \textsc{Ontributor}\yearandtitle
% add Sandra Uperv-Isor
\noindent Agnes Ursula \textsc{Thor}, Charles-Oliver \textsc{Author}, William \textsc{Riter}, Claire \textsc{Ontributor} and Sandra \textsc{Uperv-Isor}\yearandtitle
% shorten as much as necessary
\noindent Agnes Ursula \textsc{Thor}, C.-O. \textsc{Author}, W. \textsc{Riter}, C. \textsc{Ontributor} and S. \textsc{Uperv-Isor}\yearandtitle
% add Gina Raphics
\noindent A. U. \textsc{Thor}, C.-O. \textsc{Author}, W. \textsc{Riter}, C. \textsc{Ontributor}, S. \textsc{Uperv-Isor} and G. \textsc{Raphics} \yearandtitle
% shorten
\noindent A. U. \textsc{Thor}, C.-O. \textsc{Author}, W. \textsc{Riter}, C. \textsc{Ontributor}, S. \textsc{Uperv-Isor} et al. \yearandtitle
\end{document}

Bibliography:

@book{foo, Author={Agnes Ursula Thor and Charles-Oliver Author and William Riter and Claire Ontributor and Sandra Uperv-Isor and Gina Raphics}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}

Algorithm (pseudocode):

\newlength\availablewidth{\linewidth}% for instance

\ifthenelse % 1. try all full names
{\widthof{\printnames{author}} < \availablewidth}% condition
{\printnames{author}}% then
{% else
  \ifthenelse % 2. try all initial names
  {\widthof{\printnames[initials]{author}} < \availablewidth}% condition
  {\printnames[initials][]{author}}% then (advanced: expand the left-most ones again)
  {% else (automatically set `maxbibnames` locally)
    \printnames[initials][etal]{author}% (advanced: automatically determine local `minbibnames` value)
}

1 Answer 1

2
+50

Try the following code, it largely relies on your pseudo code above.

We use a \savebox to evaluate the length. We just need to set \setlength{\availablewidth}{\linewidth}.

We define a new bibmacro varwidthname. We need some new name formats for that

\DeclareNameFormat{first-last-full}{%
  \usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}
\DeclareNameFormat{first-last-inits}{%
  \usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}
\DeclareNameFormat{first-last-inits-etal}{%
  \usebibmacro{name:first-last}{#1}{#4}{#5}{#7}%
  \usebibmacro{name:andothers}}

\newlength{\availablewidth}
\setlength{\availablewidth}{\linewidth}
%\setlength{\availablewidth}{50mm}
\newcounter{templabelnamea}
\newcounter{templabelnameb}
\newcommand*{\thisnamedelim}{\multinamedelim}
\newif\ifprintouthappened
\printouthappenedfalse
\newsavebox\mybox

\makeatletter
\newbibmacro*{varwidthname}{%
  \printouthappenedfalse
  \sbox{\mybox}{\printnames[first-last-full][-\c@labelname]{labelname}}% this is the full name list
  \ifdim\wd\mybox>\availablewidth% if the full list is to long, start shortening it
    \sbox{\mybox}{\printnames[first-last-inits][-\c@labelname]{labelname}}% all initials
    \ifdim\wd\mybox<\availablewidth
      \setcounter{templabelnamea}{\c@labelname}%
      \setcounter{templabelnameb}{\c@labelname}%
      \addtocounter{templabelnameb}{-1}%
      \loop\ifnum\value{templabelnamea}>1%
        \ifnum\value{templabelnamea}=\c@labelname
          \renewcommand{\thisnamedelim}{\finalnamedelim}%
        \else
          \renewcommand{\thisnamedelim}{\multinamedelim}%
        \fi
        \sbox{\mybox}{\printnames[first-last-full][1-\value{templabelnameb}]{labelname}\thisnamedelim\printnames[first-last-inits][\value{templabelnamea}-\c@labelname]{labelname}}%
        \ifnum\wd\mybox<\availablewidth
          \printnames[first-last-full][1-\value{templabelnameb}]{labelname}\thisnamedelim\printnames[first-last-inits][\value{templabelnamea}-\c@labelname]{labelname}%
          \printouthappenedtrue
          \let\iterate\relax
        \fi
        \addtocounter{templabelnamea}{-1}%
        \addtocounter{templabelnameb}{-1}%
      \repeat
      \ifprintouthappened
      \else
        \printnames[first-last-inits][-\c@labelname]{labelname}%
        \printouthappenedtrue
      \fi
    \else
      \sbox{\mybox}{\printnames[first-last-inits][-\c@labelname]{labelname}}%
      \setcounter{templabelnamea}{\c@labelname}%
      \loop\ifnum\value{templabelnamea}>0
        \sbox{\mybox}{\printnames[first-last-inits-etal][-\value{templabelnamea}]{labelname}}%
        \ifnum\wd\mybox<\availablewidth
          \printnames[first-last-inits-etal][-\value{templabelnamea}]{labelname}%
          \printouthappenedtrue
          \let\iterate\relax
        \fi
        \addtocounter{templabelnamea}{-1}%
      \repeat
    \fi
  \else% full list is fine
    \printnames[first-last-full][-\c@labelname]{labelname}%
    \printouthappenedtrue
  \fi
  \ifprintouthappened
  \else
    \printnames[first-last-inits-etal][1-1]{labelname}%
  \fi}
\makeatother

This command will not print anything if the available width is too small to fit anything in. The command will always choose the shortest possible form (one author with initials and "et al." if applicable) if the space is too small to fit anything in.

We can the define a new cite command: \varwidthciteauthor.

\DeclareCiteCommand{\varwidthciteauthor}
  {\usebibmacro{prenote}}
  {\usebibmacro{citeindex}%
   \usebibmacro{varwidthname}}
  {\multicitedelim}
  {\usebibmacro{postnote}}

MWE

\documentclass[a4paper]{article}
\usepackage[maxnames=999]{biblatex}
\usepackage{filecontents}
\begin{filecontents*}{\jobname.bib}
@book{foo1, Author={Agnes Ursula Thor and Charles-Oliver Author and William Riter and Claire Ontributor and Sandra Uperv-Isor and Gina Raphics}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
@book{foo2, Author={Agnes Ursula Thor and Charles-Oliver Author and William Riter and Claire Ontributor and Sandra Uperv-Isor}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
@book{foo3, Author={Agnes Ursula Thor and Charles-Oliver Author and William Riter and Claire Ontributor}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
@book{foo4, Author={Agnes Ursula Thor and Charles-Oliver Author and William Riter}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
@book{foo5, Author={Agnes Ursula Thor and Charles-Oliver Author}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
@book{foo6, Author={Agnes Ursula Thor}, Year={2014}, Title={A very interesting book}, Subtitle={With subtitle}, Publisher={SUP Press}, Location={Someplace / Otherplace}}
\end{filecontents*}
\addbibresource{\jobname.bib}

\DeclareCiteCommand{\varwidthciteauthor}
  {\usebibmacro{prenote}}
  {\usebibmacro{citeindex}%
   \usebibmacro{varwidthname}}
  {\multicitedelim}
  {\usebibmacro{postnote}}

\DeclareNameFormat{first-last-full}{%
  \usebibmacro{name:first-last}{#1}{#3}{#5}{#7}}
\DeclareNameFormat{first-last-inits}{%
  \usebibmacro{name:first-last}{#1}{#4}{#5}{#7}}
\DeclareNameFormat{first-last-inits-etal}{%
  \usebibmacro{name:first-last}{#1}{#4}{#5}{#7}%
  \usebibmacro{name:andothers}}

\newlength{\availablewidth}
\setlength{\availablewidth}{\linewidth}
%\setlength{\availablewidth}{50mm}
\newcounter{templabelnamea}
\newcounter{templabelnameb}
\newcommand*{\thisnamedelim}{\multinamedelim}
\newif\ifprintouthappened
\printouthappenedfalse
\newsavebox\mybox

\makeatletter
\newbibmacro*{varwidthname}{%
  \printouthappenedfalse
  \sbox{\mybox}{\printnames[first-last-full][-\c@labelname]{labelname}}% this is the full name list
  \ifdim\wd\mybox>\availablewidth% if the full list is to long, start shortening it
    \sbox{\mybox}{\printnames[first-last-inits][-\c@labelname]{labelname}}% all initials
    \ifdim\wd\mybox<\availablewidth
      \setcounter{templabelnamea}{\c@labelname}%
      \setcounter{templabelnameb}{\c@labelname}%
      \addtocounter{templabelnameb}{-1}%
      \loop\ifnum\value{templabelnamea}>1%
        \ifnum\value{templabelnamea}=\c@labelname
          \renewcommand{\thisnamedelim}{\finalnamedelim}%
        \else
          \renewcommand{\thisnamedelim}{\multinamedelim}%
        \fi
        \sbox{\mybox}{\printnames[first-last-full][1-\value{templabelnameb}]{labelname}\thisnamedelim\printnames[first-last-inits][\value{templabelnamea}-\c@labelname]{labelname}}%
        \ifnum\wd\mybox<\availablewidth
          \printnames[first-last-full][1-\value{templabelnameb}]{labelname}\thisnamedelim\printnames[first-last-inits][\value{templabelnamea}-\c@labelname]{labelname}%
          \printouthappenedtrue
          \let\iterate\relax
        \fi
        \addtocounter{templabelnamea}{-1}%
        \addtocounter{templabelnameb}{-1}%
      \repeat
      \ifprintouthappened
      \else
        \printnames[first-last-inits][-\c@labelname]{labelname}%
        \printouthappenedtrue
      \fi
    \else
      \sbox{\mybox}{\printnames[first-last-inits][-\c@labelname]{labelname}}%
      \setcounter{templabelnamea}{\c@labelname}%
      \loop\ifnum\value{templabelnamea}>0
        \sbox{\mybox}{\printnames[first-last-inits-etal][-\value{templabelnamea}]{labelname}}%
        \ifnum\wd\mybox<\availablewidth
          \printnames[first-last-inits-etal][-\value{templabelnamea}]{labelname}%
          \printouthappenedtrue
          \let\iterate\relax
        \fi
        \addtocounter{templabelnamea}{-1}%
      \repeat
    \fi
  \else% full list is fine
    \printnames[first-last-full][-\c@labelname]{labelname}%
    \printouthappenedtrue
  \fi
  \ifprintouthappened
  \else
    \printnames[first-last-inits-etal][1-1]{labelname}%
  \fi}
\makeatother


\begin{document}
\noindent\varwidthciteauthor{foo1}

\noindent\varwidthciteauthor{foo2}

\noindent\varwidthciteauthor{foo3}

\noindent\varwidthciteauthor{foo4}

\noindent\varwidthciteauthor{foo5}

\noindent\varwidthciteauthor{foo6}
\end{document}

enter image description here


Note that you really should not use this kind of citation command in an actual document, for one the proper length to test against will be harder to establish than just \linewidth. And - this is the more important point - it will look very very inconsistent (at times you print 4 names, then again only 2, sometimes only initials, sometimes full names, at times even some kind of hybrid in between), style guides normally despise inconsistencies - especially inconsistencies of such proportion - for a good reason.

2
  • I thought it was obvious that I wouldn’t use this in citations but in the bibliography. Sorry for not being clear about that. You’re probably right, though, that consistency should be preserved. I don’t think that’s an issue with the number of names to appear before “et al.”, but it should be initials either always or never.
    – Crissov
    Commented Feb 27, 2014 at 21:35
  • 1
    @Crissov OK, but even in the bibliography this sort of inconsistency is not really what I would call aesthetically pleasing. Please have a look at the edited version of the code above, some bugs were fixed.
    – moewe
    Commented Feb 28, 2014 at 7:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .