latex中使用biblatex和tabularray生成表格文献表
这是第三篇关于latex中的表格文献表的文章。
前面介绍过两种方式《latex中生成表格形式的参考文献表》,
《latex使用bibmap生成表格形式的参考文献表》。
这一篇实际上也是利用biblatex生成,但利用新的与一般表格宏包不同的tabularray,有其特殊性,所以做专门的介绍,顺便记录实现的研究过程。
因为tabularray对于表格内容展开的特殊性要求,加上biblatex也只在latex运行时生成文献格式信息(而不是像bibtex,bibmap一样提前生成字符串列表可以直接读取),
所以两者的结合会明显不同于一般情况。下面的尝试和实现,正是考虑了两者的特点。
tabularray的特点
tabularray是先提取表格内容(先展开的),然后再根据内容拆分,做格式化。
而不像其它表格宏包,是按正常顺序展开的。
所以在表格生成过程中,如果有一些包含列标识符的命令,通常需要先展开。比如:
\newcommand*\tblrbody{
\hline
20 & 30 \\
50 & 60 \\
\hline
}
%采用如下这些方式定义表格内容也都可以使用
%\def\tblrbody
%\newrobustcmd{\tblrbody}
%\tl_new:N \l_tblrbody_tl
%\tl_set:Nn \l_tblrbody_tl
%\cs_new:Nn \tblrbody:
\begin{longtblr}[label=none,expand=\tblrbody]
{colspec = {|Q[c,m,0.10\textwidth]|Q[l,m,0.84\textwidth]|},hlines,
rowhead = 1,rows={ht=1cm}}
{\songti 序号 } & {\songti 文献目录(作者、题目、刊物名、出版时间、页次)} \\
\tblrbody
\end{longtblr}
其中,\tblrbody
要先展开,否则在tex过程中,\tblrbody和longtblr环境会同时展开,就会出现问题。
而加上expand=\tblrbody
选项就是让其先展开。
利用biblatex生成表格文献表的尝试
利用biblatex生成表格,使用longtable等表格宏包是没有问题的。
但使用tabularray 就会有问题,因为 tabularray 上述特点。
因此,我们做了尝试,详见 how to build tabular bibliography with tabularray rather than other tabular package。
得出的结论是:
-
如果在
\extblxtab@bibtabular
中,放入 tblr表,同时利用 newcommand 将biblatex的处理内容收集起来,作为tblr表格的内容,则可以生成表格,但所有的条目都是最后一个条目的信息。也就是在记录到newcommand过程中,一个关键的entrykey信息是没有被确定出来,所以在最后生成的时候,都替换为了最后一条。也就是每个条目的信息,在处理内容存储时是没有充分展开的。 -
所以我们希望对其先进行展开,比如利用edef等展开当前条目的内容,然后再收集。但显然biblatex的宏不能完全展开,所以出现了问题。
-
所以我们希望尝试是否有其他展开手段,比如将每个条目的内容写入文件。经尝试,也会面临与edef相同的问题。
因为 biblatex-ext-tabular 的作者没有时间看,没有得到有效的提示信息,所以继续思考和尝试,好在最后成功了。详见后续各节。
考虑entrykey展开的实现
根据前面尝试的结论,我们知道导致文献表没有正确生成的原因主要是宏的展开问题,但完全展开又不可能,所以我们考虑是否能做一定的展开,只要展开到能显式地给出entrykey信息就可以了。
我们考察了expandafter等相关的展开命令,以及 biblatex-ext-tabular 中 \extblxtab@bibtabular
等中使用\anchor
等宏,发现并不方便利用expandafter等做展开。
于是再考察\anchor
等宏的定义,发现其内部使用了\extblxtab@tab@item@anchor{\extblxtab@tabrow@entrykey}
,而\extblxtab@tabrow@entrykey
又是由
\gdef\extblxtab@tabrow@entrykey{##1}
定义。所以在输出的时候,实际上存在多次的展开才能到##1。这可能就是需要解决的问题。
所以,既然不能使用expandafter等做提前展开,那么我们可以考虑减少展开的层级,从而等价于提前展开。
因此,我们在\extblxtab@bibtabular
中不再使用\anchor
等高层的命令,而使用其内部定义从而减少展开,使得entrykey信息可以提前展开并记录下来。
因此,我们的方式是将原始的:
\def\do##1{%
\blx@setdefaultrefcontext{##1}%
\gdef\extblxtab@tabrow@entrykey{##1}%
\csuse{extblxtab@tabrow@format@\blx@theenv}}%
\csuse{extblxtab@env@\blx@theenv}%
\dolistloop{\blx@tempb}%
\csuse{extblxtab@endenv@\blx@theenv}%
替换为:
\newcommand\blxtablecontents{}
\def\do##1{%
\blx@setdefaultrefcontext{##1}%
\gdef\extblxtab@tabrow@entrykey{##1}%
\g@addto@macro\blxtablecontents{\leavevmode\anchor{\printfield{labelnumber}} & \driver{} \\}
}%
\dolistloop{\blx@tempb}
再改进为:
\newcommand\blxtablecontents{} %利用\blxtablecontents来存储文献的信息
\def\do##1{%
\blx@setdefaultrefcontext{##1}%
%不在使用\extblxtab@tabrow@entrykey因为多重定义会导致展开延迟
%所以导致文献表中的所有文献的信息会被最后一个文献的信息替代
%\gdef\extblxtab@tabrow@entrykey{##1}%
%使用\g@addto@macro在\blxtablecontents添加信息
%同时利用\anchor等的原始定义直接给出entrykey的信息##1
%而不用extblxtab@tabrow@entrykey避免延迟展开的问题。
\g@addto@macro\blxtablecontents{ {\leavevmode\extblxtab@tab@item@anchor
{##1}{\printfield{labelnumber}}}
& {\extblxtab@tab@item@driver
{##1}{}} \\}
}%
\dolistloop{\blx@tempb}%
利用tabularray和biblatex的表格式参考文献表的完整实现
根据上述处理,相当于在展开过程中减少了2层,所以有可能在展开时就能将各个文献条目的entrykey确定下来,从而能避免出错。
结果也验证了这一点。
一个完整的示例如下:
\documentclass{ctexart}
\usepackage[style=gb7714-2015]{biblatex}
\usepackage{biblatex-ext-tabular}
\usepackage{tabularray}
\SetTblrTemplate{head,foot}{empty}
\makeatletter
% underlying macros
\def\extblxtab@bibtabular#1{%
\blx@bibheading\blx@theheading\blx@thetitle
\blx@bibnote\blx@theprenote
\begingroup
\blx@bibinit
\let\@noitemerr\@empty
\let\blx@noitem\blx@warn@bibempty
\ifnum\bibinitsep=\z@
\let\blx@initsep\relax
\fi
\ifnum\bibnamesep=\z@
\let\blx@namesep\relax
\fi
\csuse{blx@hook@bibinit}%
\csuse{blx@hook@bibinit@next}%
% copy filtered list of entries to internal list macro
\let\blx@tempb\@empty
\def\blx@do##1{%
\blx@ifdata{##1}{%
\begingroup
\blx@getdata{##1}%
\blx@bibcheck
\iftoggle{blx@skipentry}{}{%
\global\let\blx@noitem\@empty
\listgadd\blx@tempb{##1}}%
\endgroup}{}}%
\let\blx@done\relax
\blx@listloop{#1}%
\begingroup
% sane names for wrapper macros
\def\plain {\extblxtab@tab@item@plain
{\extblxtab@tabrow@entrykey}}%
\def\plainlang {\extblxtab@tab@item@plainlang
{\extblxtab@tabrow@entrykey}}%
\def\anchor {\extblxtab@tab@item@anchor
{\extblxtab@tabrow@entrykey}}%
\def\anchorlang{\extblxtab@tab@item@anchorlang
{\extblxtab@tabrow@entrykey}}%
\def\driver {\extblxtab@tab@item@driver
{\extblxtab@tabrow@entrykey}}%
% tabular output for each item in list
% note that each cell is in its own group, hence we need \gdef to
% break out of the first cell as soon as the row format has a & in it
% since the assignment will be counted as being in the first cell
\newcommand\blxtablecontents{} %利用\blxtablecontents来存储文献的信息
\def\do##1{%
\blx@setdefaultrefcontext{##1}%
%不在使用\extblxtab@tabrow@entrykey因为多重定义会导致展开延迟
%所以导致文献表中的所有文献的信息会被最后一个文献的信息替代
%\gdef\extblxtab@tabrow@entrykey{##1}%
%使用\g@addto@macro在\blxtablecontents添加信息
%同时利用\anchor等的原始定义直接给出entrykey的信息##1
%而不用extblxtab@tabrow@entrykey避免延迟展开的问题。
\g@addto@macro\blxtablecontents{ {\leavevmode\extblxtab@tab@item@anchor
{##1}{\mkgbnumlabel{\printfield{labelnumber}}}}
& {\extblxtab@tab@item@driver
{##1}{}} \\}
}%
\dolistloop{\blx@tempb}%
%注意表格的格式直接写
\begin{longtblr}[label=none,expand=\blxtablecontents]
{colspec = {|Q[c,m,0.10\textwidth]|Q[l,m,0.84\textwidth]|},
hlines, rowhead = 1,rows={ht=1cm}}
{\songti 序号 } & {\songti 文献目录(作者、题目、刊物名、出版时间、页次)} \\
\blxtablecontents
\end{longtblr}
%不再使用原来的环境定义来输出表格
%\csuse{extblxtab@env@\blx@theenv}%
%\dolistloop{\blx@tempb}%
%\csuse{extblxtab@endenv@\blx@theenv}%
\endgroup
\blx@bibnote\blx@thepostnote
\endgroup
\endgroup % this closes the group opened by \printbibtabular
}
\makeatother
\begin{filecontents}[force]{\jobname.bib}
@book{brooks2000,
title = {Troubling {{Confessions}}: Speaking {{Guilt}} in {{Law}} and {{Literature}}},
author = {Brooks, Peter},
date = {2000},
publisher = {{University of Chicago Press}},
location = {{Chicago}}
}
@article{chamberlain_SearchCivilSociety_1993,
title = {On the {{Search}} for {{Civil Society}} in {{China}}},
author = {Chamberlain, Heath B.},
date = {1993-04},
volume = {19},
number = {2},
pages = {199--215}
}
@incollection{duwei*fokema_ZouXiangXinShiJieZhuYi_1999,
title = {走向新世界主义},
booktitle = {全球化与后殖民批评},
author = {杜威·佛克马},
editor = {王宁 and 薛晓源},
date = {1999},
pages = {247--266},
publisher = {中央编译出版社},
location = {北京}
}
@thesis{fangmingdong_LuoLongJiZhengZhiSiXiangYanJiu19131949_2000,
type = {博士},
title = {罗隆基政治思想研究(1913—1949)},
author = {方明东},
date = {2000},
institution = {北京师范大学历史系},
location = {北京}
}
@incollection{schfield_ImpactScarcityPlenty_1983,
title = {The {{Impact}} of {{Scarcity}} and {{Plenty}} on {{Population Change}} in {{England}}},
booktitle = {Hunger and {{History}}: The {{Impact}} of {{Changing Food Production}} and {{Consumption Pattern}} on {{Society}}},
author = {Schfield, R. S.},
editor = {Rotberg, R. I. and Rabb, T. K.},
date = {1983},
pages = {55--88},
publisher = {{Cambridge University Press}},
location = {{Cambridge, MA}}
}
@book{shitenghuixiu_ZhongGuoRenLiuXueRiBenShi_1982,
title = {中国人留学日本史},
author = {实藤惠秀},
translator = {谭汝谦 and 林启彦},
date = {1982},
publisher = {香港中文大学出版社},
location = {香港}
}
@article{weiliying1990,
title = {论近代西北人口波动的主要原因},
author = {{魏丽英}},
date = {1990},
journaltitle = {社会科学},
number = {6}
}
\end{filecontents}
\addbibresource{\jobname.bib}
\begin{document}
\nocite{*}
\printbibtabular
\end{document}
其结果为:
因此:使用biblatex和tabularray生成表格式文献表的方法,总结如下:
- 不再使用biblatex-ext-tabular提供的外部接口
\defbibtabular{bibtabular}
- 直接重定义
\extblxtab@bibtabular
宏,并在内部做修改,修改内容包括三个部分:- 利用新定义的
\blxtablecontents
来存储文献信息,且展开后作为表格的内容。 - 重定义
\do
使得对于entrykey的展开少几层,使得\blxtablecontents
展开时能获得对应的entrykey避免出错。 - 直接添加tblr表格用于输出文献表。
- 利用新定义的
- 正文中,正常的引用和输出,输出仍然使用
\printbibtabular
命令。
小结
本文经过实践和研究,实现了基于biblatex结合tabularray的表格式文献表,在之前的两种方法基础上提供了一种新的路径,方便用户选择使用。