4

Having spent many hours poring over examples and documentation, I have finally got my references almost up to what I require (yay!). There are still a few bits that I cannot manage, and I'm hoping that you titans of TeX will be able to assist! :) At the bottom I've given a 'minimal' working example... but it's not that short, so I'm going to try to summarise some of the issues here. Mostly it boils down to desiring if/then statements. Possibly I need to use xifthen? (I'm using DeclareBibliographyDriver.)

(1) Some of my items have volume and issue numbers. In this case, I'd like to write these as "Volume.Issue", eg 3.2. I've done this using \printfield{volume}\addperiod\printfield{number}. This is fine when there is a volume and issue number, but when there isn't it prints a fullstop (.), which I don't want. So I'd like to say

\printfield{volume}
if {'number' exists (as a field)} then {\addperiod\printfield{number}}

Similarly, I'd like to print the page numbers in brackets, eg (112--114). Again, I can use \printtext[parens]{\printfield{pages}}, but when there aren't any page numbers this just gives an empty pair of brackets, (), which I don't want. So I'd like to say

if {'pages' exists} then {\printtext[parens]{\printfield{pages}}}

I appreciate that the usual thing is not to have two questions in the same post, but given how similar these are, and that they should (hopefully) be able to both be answered together, I felt it was best to put them like this.


I now give a MWE, and a snapshot of the output.

\documentclass[]{article}

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@article{FR:giant-mixing,
    title = {The Evolution of the Mixing Rate of a Simple Random Walk on the Giant Component of a Random Graph},
    volume = {33},
    number = {1},
    journaltitle = {Random Structures \& Algorithms},
    urldate = {2018-03-22},
    date = {2008-05-12},
    pages = {68-86},
    author = {Fountoulakis, Nikolaos and Reed, Bruce A.}
}
@article{AGHH:dynamic-cm,
    archivePrefix = {arXiv},
    eprinttype = {arxiv},
    eprint = {1606.07639},
    title = {Mixing Times of Random Walks on Dynamic Configuration Models},
    date = {2016-06-24},
    author = {Avena, Luca and G{\"u}lda{\c s}, Hakan and van der Hofstad, Remco and den Hollander, Frank},
    options = {useprefix=true}
}
\end{filecontents}

\usepackage{csquotes}
\usepackage[hyperref=true,backend=biber,style=numeric]{biblatex}
\addbibresource{\jobname.bib}

\DeclareFieldFormat
    [article,book]
    {volume}{\textbf{#1}}
\DeclareFieldFormat
    {pages}{#1}

\DeclareBibliographyDriver{article}{%
    \printnames{author}%
\   \newblock
    \printtext[parens]{\printfield{year}}\addperiod%
    \newunit
    \printfield{title}
    \newunit\newblock
    \printfield{journaltitle}%
    \printfield{eprint}%\addperiod%
\   \newblock
    \printfield{volume}\addperiod\printfield{number}%
\   \newblock
    \printtext[parens]{\printfield{pages}}%
}


\begin{document}

Document...

\nocite{*}
\printbibliography

\end{document}

enter image description here

  • 1
    I assume the \ in the code are copy-and-paste artefacts and not intended, is that correct? – moewe May 9 '18 at 15:18
  • 3
    You have \iffieldundef to test for empty fields (and equivalent for lists and names). But, in your case, it seems the punctuation tracker may do the job with \printfield{volume}\setunit*{\adddot}\printfield{number} (untested). – gusbrs May 9 '18 at 15:19
  • @moewe -- they're for spacing. Is it not good practice to use them in such a way? – Sam T May 9 '18 at 15:23
  • As for the pages field, I'd say a formating directive would probably be the more adequate. – gusbrs May 9 '18 at 15:23
  • 1
    I'd like to write a comprehensive answer because I feel that a few things you do go against the spirit of biblatex. – moewe May 9 '18 at 15:48
4

biblatex has several conditionals available. Amongst them is \iffieldundef{<field>} to check if a field is undefined (a field is usually undefined if it is empty). So you could use

\iffieldundef{number}
  {}%                              <- do nothing if number does not exist
  {\addperiod\printfield{number}}% <- number exists: print it

I feel, however, that in your case you don't actually need \iffieldundef. Instead you will want to have a look at two very important concepts of biblatex styles

  • field formats \DeclareFieldFormat and
  • the punctuation tracker

Let's take \printtext[parens]{\printfield{pages}} as an example: You want to print the page number in parentheses. But as you have noticed this line of code actually gives you an empty pair of round brackets if the pages field is empty. That is because \printtext[parens] will simply wrap its argument in round brackets, even if it is empty. With our \iffieldundef you cold write \iffieldundef{pages}{}{\printtext[parens]{\printfield{pages}}}. But there is a more elegant way:

Define that you want to see the pages of @articles in round brackets with

 \DeclareFieldFormat[article]{pages}{\mkbibparens{#1}}

and then simply use

 \printfield{pages}

With the first line you tell biblatex to put the page numbers in parentheses and the second line tells biblatex to print the page numbers. If the pages field is not present nothing happens.

Something similar could be done for the year, but biblatex has a more complex set-up to handle dates, so that is slightly more involved. I would probably go with

\DeclareFieldFormat[article]{date}{\mkbibparens{#1}}

for the format and then \printdate or better

\usebibmacro{date}

The punctuation tracker makes it much easier to take care of punctuation and spaces. You won't have to use \iffieldundef for the dot between volume and number, and you won't have to use \ to provide space.

The tracker is explained in §4.11.7 Using the Punctuation Tracker of the biblatex documentation. The section is a few pages long (so is this answer ...), but it comes with many examples and is definitely worth a read. The idea of the punctuation tracker is that biblatex does not immediately print any punctuation it encounters, instead it remembers the punctuation and inserts it when it prints text the next time. This avoids double punctuation. The first takeaway from the section is that you will almost always want to use \setunit for punctuation, punctuation commands should only rarely appear outside of \setunit.

\setunit takes the supplied punctuation command and saves it in the punctuation buffer. The buffer is used and cleared the next time biblatex prints anything (using \print...). Subsequent \setunits overwrite the buffer.

So you could say

\printfield{eprint}%
\setunit{\addspace}\newblock
\printfield{pages}%

to print the eprint field followed by a space and then the pages field. You won't run into trouble if one of the two fields is not printed.

The go-to punctuation between two fields is \newunit\newblock. \newunit essentially does a \setunit on the punctuation defined in \newunitpunct and \newblock adds an internal marker that a new block/'chunk' starts here. The behaviour of blocks can be modified with the block option.

Using the punctuation tracker \printfield{volume}\addperiod\printfield{number}% would become

\printfield{volume}%
\setunit*{\adddot}
\printfield{number}%

Here we used the starred version \setunit*, it only inserts a value into the buffer if the last \print... command before it did anything (so it is like \setunit with an added check: If the last \print... printed anything, \setunit* behaves exactly like \setunit, otherwise it discards its argument). So no dot will be inserted into the buffer if volume is empty.


If you write bibliography drivers it is a good idea to try and model them on the standard drivers found in standard.bbx. The drivers in standard.bbx don't actually call many \print... directives directly, instead they call other bibmacros to do this. A driver should always call the two macros bibindex and begentry at the beginning and must call finentry at the end. The finentry bibmacro calls the \finentry macro that does some clean-up work.

Your set-up could look like

\DeclareFieldFormat[article]{date}{\mkbibparens{#1}}
\DeclareFieldFormat[article,book]{volume}{\mkbibbold{#1}}
\DeclareFieldFormat[article]{pages}{\mkbibparens{#1}}

\renewbibmacro*{volume+number+eid}{%
  \printfield{volume}%
  \setunit*{\adddot}%
  \printfield{number}%
  \setunit{\addcomma\space}%
  \printfield{eid}}

\DeclareBibliographyDriver{article}{%
  \usebibmacro{bibindex}%
  \usebibmacro{begentry}%
  \usebibmacro{author}%
  \setunit{\addspace}%
  \usebibmacro{date}%
  \newunit\newblock
  \usebibmacro{title}%
  \newunit\newblock
  \printfield{journaltitle}%
  \setunit{\addspace}%
  \usebibmacro{volume+number+eid}%
  \newunit\newblock
  \usebibmacro{eprint}%
  \setunit{\addspace}\newblock
  \printfield{pages}%
  \usebibmacro{finentry}%
}

In fact this could have been changed to resemble the @article driver in standard.bbx even more, but then more things would have been hidden.

In fact I believe that you can get the output you want without even redefining a single driver only by changing bibmacros and a bit of sly trickery.


Let me close with a few of my favourite biblatex pet peeves

  • The option hyperref=true is normally not better than the default hyperref=auto. The only difference between the two is that hyperref=true writes a warning to the .log file if hyperref is not loaded. Other than that they behave identical. In particular both enable hyperlinking if and only if hyperref is loaded.

  • A paper that was published on the arXiv but not in any journal is not an @article - it is an @online entry. Only papers that actually have a non-empty journal field can be @articles. See Format of @article without journal title field in biblatex bibliography entries

| improve this answer | |
  • Thanks very much for your very thorough response! I do have a couple of questions, which I'll put in individual comments for ease of reading. – Sam T May 9 '18 at 16:58
  • When I do Volume.Number, you said to use the stared \setunit, because this only prints the \addperiod if the previous \print actually did something. How come if I have an bibitem with a Volume entry but no Number entry then it doesn't print the fullstop? The Volume prints, so surely then the fullstop (period) is printed under \setunit*? Do you mean instead that the \setunit* only prints if the next one actually prints? – Sam T May 9 '18 at 17:03
  • @SamT My description of \setunit* might not have been clear enough, sorry, I have edited the answer with slightly more detail. If you have a volume, but no number, \setunit* adds its punctuation to the buffer (it acts like \setunit, because the last \print... actually printed something), but before the buffer is inserted the next \setunit overwrites the buffer and so you get to see a period and a space. Because the buffer is overwritten, the last \setunit/\newunit before a \print... wins. – moewe May 9 '18 at 19:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.