5

I'm trying to arrange a list of sources which, from the point of view of their content, is hierarchically grouped. Specifically "archives" and "collection within archives". I'd like to express this relation within my bibliography/biblist using biblatex. "archives" are just a title, "collection within archives" have a shorthand which is the way they will be referred to in the text. So I'm looking for something "biblist-like".

I've been struggling with defbibenvironment but could not reach a satisfactory result yet. Ideally, I'm trying to set different (independent) indents/margins/etc for each hierarchical level of sources. A sort of a nested list within defbibenvironment.

The best result I could reach was by mixing elements of the default defbibenvironments for bibliography and shorthands, conditioning settings for each case:

\RequirePackage{filecontents}

\begin{filecontents}{\jobname.bib}
@customa{natarch,
    title = {National Archives. But with a title long enough to span two lines. Yet a little longer.},
    sortshorthand = {NatArch},
}

@customb{natarch:presabc,
    title = {President ABC Papers. A subtitle to let it reach a second line.},
    shorthand = {NatArch -- ABC},
    sortshorthand = {NatArch -- ABC},
    xref = {natarch},
}

@customb{natarch:mintru,
    title = {Ministry of Truth Papers. Fully and continuously updated.},
    shorthand = {NatArch -- MT},
    sortshorthand = {NatArch -- MT},
    xref = {natarch},
}

@customa{libcong,
    title = {Library of Congress},
    sortshorthand = {LibCon},
}

@customb{libcong:digcoll,
    title = {Digital Collections of the Library of Congress. But with a title long enough to span two lines.},
    shorthand = {LibCon -- DC},
    sortshorthand = {LibCon -- DC},
    xref = {libcong},
}
\end{filecontents}

\documentclass{article}

\usepackage[style=authortitle,minxrefs=1]{biblatex}

\usepackage{calc}

\addbibresource{\jobname.bib}

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareBibliographyDriver{primary}{%
    \usebibmacro{bibindex}%
    \usebibmacro{begentry}%
    \printfield{title}%
    \usebibmacro{finentry}}

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareSortingTemplate{primary}{
  \sort{
    \field{sortshorthand}
  }
  \sort{
    \field{shorthand}
  }
}

\defbibenvironment{primary}
  {\list
       {}
       {\setlength{\leftmargin}{\bibhang+\biblabelsep}%
        \setlength{\itemindent}{-\leftmargin}%
        \setlength{\itemsep}{\bibitemsep}%
        \setlength{\parsep}{\bibparsep}%
        \renewcommand*{\makelabel}[1]{##1\hss}}}
  {\endlist}
  {\ifentrytype{customa}
      {\item}{}
   \ifentrytype{customb}
      {\setlength{\labelwidth}{\shorthandwidth}%
            \setlength{\labelsep}{\biblabelsep}%
            \setlength{\itemindent}{\shorthandwidth-.5\biblabelsep}%
       \item[\printfield{shorthand}]}{}
   }

\begin{document}

\nocite{libcong:digcoll,natarch:mintru,natarch:presabc}
\printbiblist[title={Primary sources}]{primary}
\printshorthands
\printbibliography

\end{document}

Which results in:

enter image description here

The would be desired result is the first one "Primary sources", the others are there for comparison. With this approach I could not escape from a global setting for leftmargin, which is the same for the two type of entries. I could only change their respective indents.

Ideally, I'd like the "archive" to behave just like an entry in a bibliography (as in "References") and the "archive in collection" to behave just like a list of shorthands (as in "Abbreviations", that is, with left margin at shorthandwidth+biblabelsep), with an extra indent to emphasize the grouping.

Other approaches considered were:

  • Use of biblatex's related facilities, as with multivolume relatedtype. This has two problems for my settings. First, the indent for the related entries would be not ideal for the multiline case. Second, and more important for me, is that I need the hook for the individual child entry in the bibliography for linking purposes from the citations, and I don't want it in duplicity.

  • Use of some mechanism like bibbycategory. That would actually be a good idea, having the mother entry as a bibheading and a proper biblist for the child entries. However, my list of sources is longer and I don't want to populate it manually. Furthermore, I wouldn't like to restrict the use of the category mechanism just for that (though I probably could restrain the loop for the categories of interest, I suppose). Anyway, I couldn't devise a way to populate it automatically (DeclareSourcemap, as far as I understand, cannot create a category, so I was already stuck at this point).

  • I've also tried something in the line of Guido's answer to biblatex, biber, how to create a loop to \printbibliography year after year from xxxx to yyyy?, which involves removing the \item from defbibenvironment and dealing with it at the bibentry, but I could not get any working solution in these lines.

  • I've also considered Philippe Goutet's answer to Change hanging indent in description list, but it is beyond my understanding and thus I cannot really adapt it for my purposes, if that is indeed possible.

  • I've also attempted to start from a standard printbibliography and to redefine the bibdriver of the child entry to try to emulate a biblist entry with mboxes, parboxes and tabulars. But this approach seems to stretch by a long shot the bibdriver, and biblatex loses the grip on vertical spacing and punctuation. Though these issues might be eventually circumvented, I thought this line to be not a very wise one.

2

Well, since I asked I've reached a satisfactory, if not ideal, solution to my own question. I'll post it as an answer, for it might be useful. But other answers continue to be very welcome.

Update: I could improve this solution (see update below) to very good results which, from the point of view of output and use, leaves nothing to be desired. But, it is far from straightforward. I'll leave this answer accepted, for it does solve the OP, but will be glad to change this if someone wants to try their hands on something simpler.

I've managed to arrange a solution which intertwines the groups of entries of interest, issuing one printbiblist for each, and loops along a set of mother entries provided in a list manually supplied. Some checks were also introduced in order to be sure nothing has been forgotten.

\RequirePackage{filecontents}

\begin{filecontents}{\jobname.bib}
@customa{natarch,
    title = {National Archives. But with a title long enough to span two lines. Yet a little longer},
    sortshorthand = {NatArch},
}

@customb{natarch:presabc,
    title = {President ABC Papers. A subtitle to let it reach a second line},
    shorthand = {NatArch -- ABC},
    sortshorthand = {NatArch -- ABC},
    xref = {natarch},
}

@customb{natarch:mintru,
    title = {Ministry of Truth Papers. Fully and continuously updated},
    shorthand = {NatArch -- MT},
    sortshorthand = {NatArch -- MT},
    xref = {natarch},
}

@customa{libcong,
    title = {Library of Congress},
    sortshorthand = {LibCon},
}

@customb{libcong:digcoll,
    title = {Digital Collections of the Library of Congress. But with a title long enough to span two lines},
    shorthand = {LibCon -- DC},
    sortshorthand = {LibCon -- DC},
    xref = {libcong},
}

\end{filecontents}

\documentclass{article}

\usepackage[style=authortitle,minxrefs=1]{biblatex}

\usepackage{calc}

\addbibresource{\jobname.bib}

% Providing bibdrivers

\DeclareBibliographyDriver{customa}{%
    \usebibmacro{bibindex}%
    \usebibmacro{begentry}%
    \printfield{title}%
    \newunit\newblock
    \printlist{location}%
    \usebibmacro{finentry}}

\DeclareBibliographyDriver{customb}{%
    \usebibmacro{bibindex}%
    \usebibmacro{begentry}%
    \printfield{title}%
    \usebibmacro{finentry}}

%Providing structure for biblist “primary”

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareBibliographyDriver{primary}{%
    \usedriver{}{\thefield{entrytype}}%
    \usebibmacro{finentry}}

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareSortingTemplate{primary}{
  \sort{
    \field{sortshorthand}
  }
  \sort{
    \field{shorthand}
  }
}

% Defining the two bibenvironments to be used in “primary” biblists

\defbibenvironment{primaryarchive}
  {\list
     {}
     {\setlength{\leftmargin}{\bibhang}%
      \setlength{\itemindent}{-\leftmargin}%
      \setlength{\itemsep}{\bibitemsep}%
      \setlength{\parsep}{\bibparsep}%
      \setlength{\topsep}{0pt}%
      \setlength{\partopsep}{2pt plus1pt minus1pt}}}
  {\endlist}
  {\item}

\defbibenvironment{primary}
  {\list
     {\printfield[shorthandwidth]{shorthand}}
     {\setlength{\labelwidth}{\shorthandwidth}%
      \setlength{\leftmargin}{\labelwidth}%
      \setlength{\labelsep}{\biblabelsep}%
      \addtolength{\leftmargin}{.5\bibhang}%
      \addtolength{\leftmargin}{\labelsep}%
      \setlength{\itemsep}{\bibitemsep}%
      \setlength{\parsep}{\bibparsep}%
      \setlength{\topsep}{0pt}%
      \setlength{\partopsep}{0pt}%
      \renewcommand*{\makelabel}[1]{##1\hss}}}
  {\endlist}
  {\item}

% Providing structure for a check if entries have not been forgotten

\DeclareBibliographyCategory{primarysources}
\DeclareBibliographyCategory{alreadyinprimarylists}

\AtDataInput[customa]{\addtocategory{primarysources}{\thefield{entrykey}}}
\AtDataInput[customb]{\addtocategory{primarysources}{\thefield{entrykey}}}

\AtEveryBiblistitem{primary}{\addtocategory{alreadyinprimarylists}{\thefield{entrykey}}}

% The individual biblists are screened by keyword, this adds a keyword for both mother and child entrys equal to the entrykey of the mother entry

\DeclareSourcemap{
    \maps[datatype=bibtex]{
        \map[overwrite]{
            \pertype{customb}
            \step[fieldsource=xref]
            \step[fieldset=keywords,fieldvalue={,},append]
            \step[fieldset=keywords,origfieldval,append]
            \step[fieldsource=keywords,match=\regexp{^,},replace={}]
        }
        \map[overwrite]{
            \pertype{customa}
            \step[fieldsource=entrykey]
            \step[fieldset=keywords,fieldvalue={,},append]
            \step[fieldset=keywords,origfieldval,append]
            \step[fieldsource=keywords,match=\regexp{^,},replace={}]
        }
    }
}

% Provides commands to print mother and child entries for each group, and a command to iterate along them

\newcommand{\printarchive}[1]{%
    \printbiblist[env=primaryarchive,heading=none,type=customa,keyword=#1]{primary}
    \printbiblist[heading=none,type=customb,keyword=#1]{primary}%
}
\newcommand{\printarchives}[1]{%
    \forcsvlist{\printarchive}{#1}%
}

% Just formatting
\DeclareFieldFormat[customa,customb]{title}{#1}

\begin{document}

\nocite{libcong:digcoll,natarch:mintru,natarch:presabc}

% Prints primary sources
\printbibheading[title={Primary sources}]
\printarchives{libcong,natarch}

% Checks if nothing has been forgotten
\defbibnote{warningmissingprimsources}{\mkbibbold{These entries should have been printed in the primary sources biblists above. If their were not, they are listed here which means that something is amiss and these entries should be handled until this warning is gone.}}
\printbibliography[title={Missing primary sources},prenote=warningmissingprimsources,category=primarysources,notcategory=alreadyinprimarylists]

% For comparison
\printshorthands
\printbibliography

\end{document}

The result is as desired:

enter image description here

What's still missing:

  • The list of mother entries is still provided manually. This means I had to introduce some convoluted checks to deal with eventual "stray entries". More importantly, the sorting is also done by the order of the manually provided list. There is probably a way to generate a list with all customa entries, sort it according to a defined sorting scheme and supply it to printarchives. But I couldn't figure that out. So, other answers which complete this approach, or which take another approach altogether, continue to be very welcome, as initially stated.

Update: I found a way to generate the sorted list of customa entries. It involves creating an auxiliary printbiblist, with bibfilter, sorting scheme and so on, do a false run of this biblist and use it to populate an etoolbox list, which is then fed to printarchives. For those eventually interested:

\RequirePackage{filecontents}

\begin{filecontents}{\jobname.bib}
@customa{natarch,
    title = {National Archives. But with a title long enough to span two lines. Yet a little longer},
    sortshorthand = {NatArch},
}

@customb{natarch:presabc,
    title = {President ABC Papers. A subtitle to let it reach a second line},
    shorthand = {NatArch -- ABC},
    sortshorthand = {NatArch -- ABC},
    xref = {natarch},
}

@customb{natarch:mintru,
    title = {Ministry of Truth Papers. Fully and continuously updated},
    shorthand = {NatArch -- MT},
    sortshorthand = {NatArch -- MT},
    xref = {natarch},
}

@customa{libcong,
    title = {Library of Congress},
    sortshorthand = {LibCon},
}

@customb{libcong:digcoll,
    title = {Digital Collections of the Library of Congress. But with a title long enough to span two lines},
    shorthand = {LibCon -- DC},
    sortshorthand = {LibCon -- DC},
    xref = {libcong},
}

\end{filecontents}

\documentclass{article}

\usepackage[style=authortitle,minxrefs=1]{biblatex}

\usepackage{calc}

\addbibresource{\jobname.bib}

% Providing bibdrivers

\DeclareBibliographyDriver{customa}{%
    \usebibmacro{bibindex}%
    \usebibmacro{begentry}%
    \printfield{title}%
    \newunit\newblock
    \printlist{location}%
    \usebibmacro{finentry}}

\DeclareBibliographyDriver{customb}{%
    \usebibmacro{bibindex}%
    \usebibmacro{begentry}%
    \printfield{title}%
    \usebibmacro{finentry}}

%Providing structure for biblist “primary”

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareBibliographyDriver{primary}{%
    \usedriver{}{\thefield{entrytype}}%
    \usebibmacro{finentry}}

\DeclareBiblistFilter{primary}{
    \filteror{
        \filter[type=type,filter=customa]
        \filter[type=type,filter=customb]
    }
}

\DeclareSortingTemplate{primary}{
  \sort{
    \field{sortshorthand}
  }
  \sort{
    \field{shorthand}
  }
}

% Defining the two bibenvironments to be used in “primary” biblists

\defbibenvironment{primaryarchive}
  {\list
     {}
     {\setlength{\leftmargin}{\bibhang}%
      \setlength{\itemindent}{-\leftmargin}%
      \setlength{\itemsep}{\bibitemsep}%
      \setlength{\parsep}{\bibparsep}%
      \setlength{\topsep}{0pt}%
      \setlength{\partopsep}{2pt plus1pt minus1pt}}}
  {\endlist}
  {\item}

\defbibenvironment{primary}
  {\list
     {\printfield[shorthandwidth]{shorthand}}
     {\setlength{\labelwidth}{\shorthandwidth}%
      \setlength{\leftmargin}{\labelwidth}%
      \setlength{\labelsep}{\biblabelsep}%
      \addtolength{\leftmargin}{.5\bibhang}%
      \addtolength{\leftmargin}{\labelsep}%
      \setlength{\itemsep}{\bibitemsep}%
      \setlength{\parsep}{\bibparsep}%
      \setlength{\topsep}{0pt}%
      \setlength{\partopsep}{0pt}%
      \renewcommand*{\makelabel}[1]{##1\hss}}}
  {\endlist}
  {\item}


% Provides structure for "auxiliary" biblist
% Used to generate a sorted list of customa (mother) entries

\DeclareBiblistFilter{auxiliary}{
        \filter[type=type,filter=customa]
}

\DeclareSortingTemplate{auxiliary}{
    \sort{
        \field{title}
    }
}

\DeclareBibliographyDriver{auxiliary}{}

\defbibenvironment{auxiliary}
  {\list
     {}
     {\setlength{\itemsep}{0pt}%
      \setlength{\parsep}{0pt}%
      \setlength{\topsep}{0pt}%
      \setlength{\partopsep}{0pt}}}
  {\endlist}
  {}

\AtEveryBiblistitem{auxiliary}{\listxadd{\sortedcustomalist}{\thefield{entrykey}}}

% The individual biblists are screened by keyword, this adds a keyword for both mother and child entrys equal to the entrykey of the mother entry

\DeclareSourcemap{
    \maps[datatype=bibtex]{
        \map[overwrite]{
            \pertype{customb}
            \step[fieldsource=xref]
            \step[fieldset=keywords,fieldvalue={,},append]
            \step[fieldset=keywords,origfieldval,append]
            \step[fieldsource=keywords,match=\regexp{^,},replace={}]
        }
        \map[overwrite]{
            \pertype{customa}
            \step[fieldsource=entrykey]
            \step[fieldset=keywords,fieldvalue={,},append]
            \step[fieldset=keywords,origfieldval,append]
            \step[fieldsource=keywords,match=\regexp{^,},replace={}]
        }
    }
}

% Provides an invisible box to perform false run of "auxiliary"
% from https://tex.stackexchange.com/a/22358/105447
\def\falserunbiblist#1{\edef\theprevdepth{\the\prevdepth}\nointerlineskip
  \vbox to0pt{#1\vss}\prevdepth=\theprevdepth}

% Provides commands to print mother and child entries for each group, and a command to iterate along them

\newcommand{\printarchive}[1]{%
    \printbiblist[env=primaryarchive,heading=none,type=customa,keyword=#1]{primary}
    \printbiblist[heading=none,type=customb,keyword=#1]{primary}%
}

\newcommand*{\sortedcustomalist}{}

\newcommand{\printarchives}{%
    \falserunbiblist{\printbiblist[heading=none]{auxiliary}} % This generates the list for the loop below
    \forlistloop{\printarchive}{\sortedcustomalist}
}

% Just formatting
\DeclareFieldFormat[customa,customb]{title}{#1}

\begin{document}

\nocite{libcong:digcoll,natarch:mintru,natarch:presabc}

% Prints primary sources
\printbibheading[title={Primary sources}]
\printarchives

% For comparison
\printshorthands
\printbibliography

\end{document}
  • Very nice job! Could you please expand your example with one more level of nesting? Something like: archive (customa) -> collection (customb) -> archival_unit (customc) which is a child of collection. – andc Jun 14 '19 at 11:20
  • @andc A fellow historian I see ;-). This approach is tricky enough as it is... I do recommend the one in the accepted answer, it is much cleaner. Note also that there is an open feature request on the matter. If you feel it is relevant, you might want to weight in there, for devs to be able to assess the demand towards this requested feature. – gusbrs Jun 14 '19 at 15:52
  • I think there is a small problem in the accepted answer with the sorting. customa and customb items are sorted together, there is no second level of the sorting. So, if you change sortkey of the natarch to sortkey = {a} leaving everything other unchanged, it will generate incorrect result. In your previous solution a sorting ot the customb items was made inside appropriate customa item. – andc Jun 26 '19 at 13:12
  • @andc Well, but yes, the solution does depend on the sortkey being properly set up. If you change it to an arbitrary value, it's no wonder the results will follow the same path. I've been using that structure for some time now in my production settings, with very good results. – gusbrs Jun 26 '19 at 15:35
  • @andc Of course, you certainly can improve that with a proper sorting template to sort it in a more automatic fashion with the proper fields of interest. That was just not the focus of the problem here. – gusbrs Jun 26 '19 at 15:40
1

I was able to achieve a massive simplification of the solution to this problem (compare with https://tex.stackexchange.com/a/409970/105447) using @moewe's approach at Indent of members of a set in biblatex, which allows us to have a sublist within the bibenvironment (in this case, placed in the bibdriver).

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@customa{natarch,
  title = {National Archives. But with a title long enough to span two lines. Yet a little longer.},
  sortkey = {NatArch},
}

@customb{natarch:presabc,
  title = {President ABC Papers. A subtitle to let it reach a second line.},
  shorthand = {NatArch -- ABC},
  sortkey = {NatArch -- ABC},
  xref = {natarch},
}

@customb{natarch:mintru,
  title = {Ministry of Truth Papers. Fully and continuously updated.},
  shorthand = {NatArch -- MT},
  sortkey = {NatArch -- MT},
  xref = {natarch},
}

@customa{libcong,
  title = {Library of Congress},
  sortkey = {LibCon},
}

@customb{libcong:digcoll,
  title = {Digital Collections of the Library of Congress. But with a title long enough to span two lines.},
  shorthand = {LibCon -- DC},
  sortkey = {LibCony -- DC},
  xref = {libcong},
}
\end{filecontents}

\usepackage[style=authortitle,minxrefs=1,locallabelwidth=true]{biblatex}
\addbibresource{\jobname.bib}

\DeclareBibliographyDriver{customa}{%
  \usebibmacro{bibindex}%
  \usebibmacro{begentry}%
  \printfield{title}%
  \usebibmacro{finentry}}

\newenvironment{subbiblist}
  {\begin{list}
      {\printfield[shorthandwidth]{shorthand}}%
      {\setlength{\labelwidth}{\shorthandwidth}%
      \setlength{\leftmargin}{\labelwidth}%
      \setlength{\labelsep}{\biblabelsep}%
      \addtolength{\leftmargin}{\labelsep}%
      \setlength{\itemsep}{\bibitemsep}%
      \setlength{\parsep}{\bibparsep}%
      \renewcommand*{\makelabel}[1]{##1\hss}}}
  {\end{list}}

\makeatletter
\DeclareBibliographyDriver{customb}{%
  \usebibmacro{bibindex}%
  % from https://tex.stackexchange.com/a/463414/105447
  % don't issue \leavevmode inside here
  \let\blx@leavevmode\relax
  % get rid of the \leavevmode from the beginning of the entry
  % this avoids an empty line before the enumerate
  \vspace{-\dimexpr\baselineskip+\itemsep\relax}%
  \begin{subbiblist}
    \item
    \usebibmacro{begentry}%
    \printfield{title}%
    \usebibmacro{finentry}%
  \end{subbiblist}%
}
\makeatother

\begin{document}

\nocite{*}
\printbibliography

\end{document}

enter image description here

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.