我正在尝试改进我的套餐multifootnote
,希望当脚注标记分布在多页时,它能够正确地将脚注文本放在相应的页面上。为此,我尝试将页码、脚注标记和文本信息写入辅助文件,然后通过主命令将它们读回。
但是,当前测试版本在第二次运行时会显示以下错误(这似乎表明从 aux 文件读回的信息有问题):
TeX capacity exceeded, sorry [input stack size=10000].
\l_tmpa_clist ->\ref *{\l_tmpa_clist
经过一番调试,问题似乎出在\__multifootnote_print_footnotetext:nn
(见下面的代码),如果我注释掉相应的部分,代码就可以正常运行(尽管没有这个包最重要的功能)。这是一个实际打印的命令\footnotetext
。然而,我没有修改这部分代码,它是直接从旧版本复制而来的,唯一的变化是我决定这次将代码做成一个单独的宏。
一开始我以为是因为\g__multifootnote_mark_internal_per_page_seq
是空的,这是要打印的脚注标记和文本的序列,这让我想问先前这个问题但从它的回答来看,问题似乎其实不在这里。
有什么建议可以指导我下一步该做什么吗?目前我不知道为什么会出现此错误,而且由于打印脚注文本是此修改的关键,如果无法打印结果,我甚至不知道如何猜测哪里出了问题。
这是一个 MWE。
\documentclass{article}
\usepackage[paperwidth=5cm,paperheight=5cm]{geometry}
\usepackage{multifootnote,hyperref}
\begin{document}
% First approach
Lorem ipsum\footnotenumber[fn1] dolor\footnotenumber[fn2] sit amet\footnotenumber[fn3], \clearpage
consectetur\footnotenumber[fn4] adipiscing elit\footnotenumber[fn5].
\multifootnote[fn1,fn2,fn5]{This is a footnote for demonstration.}
\multifootnote[fn1,fn3,fn4]{This is another footnote for demonstration.}
\end{document}
以下是测试版本multifootnote.sty
:
\NeedsTeXFormat{LaTeX2e}[2022-06-01]
\ProvidesExplPackage
{multifootnote}
{2024/03/20*} {}
{Multiple numbers for the same footnote}
\msg_new:nnn { multifootnote }
{ non-compatible-package }
{ "multifootnote" ~ is ~ not ~ compatible ~ with ~ the ~ package ~ "#1". }
\hook_gput_code:nnn { begindocument/before } { multifootnote }
{
\@ifpackageloaded { footnotebackref }
{
\msg_warning:nnn { multifootnote } { non-compatible-package } { footnotebackref }
} {}
}
\dim_new:N \l_multifootnote_space_after_comma_dim
\dim_set:Nn \l_multifootnote_space_after_comma_dim { 0.2ex }
\bool_new:N \l__multifootnote_left_indent_bool
\bool_set_false:N \l__multifootnote_left_indent_bool
\dim_new:N \l__multifootnote_left_indent_dim
\keys_define:nn { multifootnote }
{
, left-align .bool_set:N = \l__multifootnote_left_align_bool
, left-align .initial:n = { false }
, left~align .bool_set:N = \l__multifootnote_left_align_bool
, left align .bool_set:N = \l__multifootnote_left_align_bool
, left-indent .code:n = {
\bool_set_true:N \l__multifootnote_left_indent_bool
\dim_set:Nn \l__multifootnote_left_indent_dim { #1 }
}
, left-indent .default:n = { 1.5em }
, left~indent .code:n = {
\bool_set_true:N \l__multifootnote_left_indent_bool
\dim_set:Nn \l__multifootnote_left_indent_dim { #1 }
}
, left~indent .default:n = { 1.5em }
, left indent .code:n = {
\bool_set_true:N \l__multifootnote_left_indent_bool
\dim_set:Nn \l__multifootnote_left_indent_dim { #1 }
}
, left indent .default:n = { 1.5em }
, unknown .code:n = {}
}
\ProcessKeyOptions [ multifootnote ]
\cs_new_protected:Nn \multifootnote_make_footnote_left_align:n
{
\renewcommand { \@makefntext } [1]
{
\skip_horizontal:n { #1 }
\tl_if_blank:eF { \text_expand:n { \@thefnmark } }
{
\@makefnmark
\nobreakspace
}
##1
}
}
\bool_if:NT \l__multifootnote_left_align_bool
{
\multifootnote_make_footnote_left_align:n { 0pt }
}
\bool_if:NT \l__multifootnote_left_indent_bool
{
\multifootnote_make_footnote_left_align:n { \l__multifootnote_left_indent_dim }
}
\tl_new:N \l__multifootnote_tmp_tl
\seq_new:N \l__multifootnote_tmp_seq
% First approach
\prop_new:N \g__multifootnote_mark_pagenumber_prop
\seq_new:N \g__multifootnote_mark_internal_seq
\seq_new:N \g__multifootnote_mark_internal_per_page_seq
\cs_new:Nn \__multifootnote_process_per_page_mark:nnN
{
\tl_clear:N #3
\clist_clear:N \l_tmpa_clist
\clist_map_inline:nn { #1 }
{
\prop_get:NnN \g__multifootnote_mark_pagenumber_prop { ##1 } \l_tmpa_tl
% only keep those labels that appear on the current page
\str_if_eq:eeT { \thepage } { \l_tmpa_tl }
{
\clist_put_right:Nn \l_tmpa_clist { ##1 }
}
}
\tl_put_right:No #3 { { \l_tmpa_clist } }
\tl_put_right:Nn #3 { { #2 } }
}
\hook_gput_code:nnn { shipout } { multifootnote }
{
% reinitialize the per page footnotetext list
\seq_gset_eq:NN \g__multifootnote_mark_internal_per_page_seq \g__multifootnote_mark_internal_seq
% leave only those labels appeared on the current page
\seq_clear:N \l__multifootnote_tmp_seq
\seq_map_inline:Nn \g__multifootnote_mark_internal_per_page_seq
{
\__multifootnote_process_per_page_mark:nnN #1 \l__multifootnote_tmp_tl
\seq_put_right:No \l__multifootnote_tmp_seq { \l__multifootnote_tmp_tl }
}
\seq_gset_eq:NN \g__multifootnote_mark_internal_per_page_seq \l__multifootnote_tmp_seq
}
\NewDocumentCommand \@multifootnote@add@to@mark@pagenumber@list { m m }
{
\prop_gput:Nnn \g__multifootnote_mark_pagenumber_prop { #1 } { #2 }
}
\NewDocumentCommand \multifootnotemark { O{} }
{
% print the footnote mark
\footnotemark
\group_begin:
\tl_if_blank:nF { #1 }
{
\addtocounter { footnote } { -1 }
\refstepcounter { footnote }
\@ifpackageloaded { hyperref }
{
\cs_gset_eq:NN \@currentHref \Hy@footnote@currentHref
} {}
\label { #1 }
}
\group_end:
% write the corresponding pagenumber to aux file
\iow_now:cx { @auxout }
{
\@multifootnote@add@to@mark@pagenumber@list { #1 } { \thepage }
}
% print the corresponding footnote text
\seq_map_inline:Nn \g__multifootnote_mark_internal_per_page_seq
{
\__multifootnote_print_footnotetext:nn ##1
}
}
\cs_new:Nn \__multifootnote_print_footnotetext:nn
{
\clist_clear:N \l_tmpa_clist
\clist_clear:N \l_tmpb_clist
\tl_if_blank:nF { #1 }
{
\clist_map_inline:nn { #1 }
{
\@ifpackageloaded { hyperref }
{
\clist_put_right:Nn \l_tmpa_clist
{ \ref*{ ##1 } }
\clist_put_right:Nn \l_tmpb_clist
{ \Hy@raisedlink { \hypertarget { \getrefbykeydefault{##1}{anchor}{Doc-Start} } {} } }
}
{
\clist_put_right:Nn \l_tmpa_clist
{ \ref { ##1 } }
}
}
\def \thefootnote { \clist_use:Nn \l_tmpa_clist { , \skip_horizontal:n { \l_multifootnote_space_after_comma_dim } } }
\@ifpackageloaded { hyperref }
{
\xdef \Hy@footnote@currentHref { x\Hy@footnote@currentHref }
} {}
\footnotetext { \clist_use:Nn \l_tmpb_clist {} \ignorespaces #2 }
}
}
\NewCommandCopy \footnotenumber \multifootnotemark
\NewDocumentCommand \@multifootnote@add@to@mark@text@list { m m }
{
\seq_gput_right:Nn \g__multifootnote_mark_internal_seq { { #1 } { #2 } }
}
\NewDocumentCommand \multifootnotetext { O{} m }
{
\tl_if_blank:nTF { #1 }
{
\footnotetext { #2 }
}
{
\iow_now:cx { @auxout }
{
\@multifootnote@add@to@mark@text@list { #1 } { #2 }
}
}
}
% Second approach
% \seq_new:N \g__multifootnote_tag_internal_seq
% \seq_new:N \g__multifootnote_tag_internal_per_page_seq
% \hook_gput_code:nnn { shipout } { multifootnote }
% {
% \seq_gset_eq:NN \g__multifootnote_tag_internal_per_page_seq \g__multifootnote_tag_internal_seq
% }
\NewDocumentCommand \multifootnotetag { m }
{
\footnotemark
\group_begin:
\def \thefootnote {}
\footnotetext { \vspace { -\baselineskip } }
\group_end:
\clist_map_inline:nn { #1 }
{
\cs_if_exist:cTF { multifootnote-tag- ##1 }
{
\tl_gput_right:cx { multifootnote-tag- ##1 } { , \skip_horizontal:n { \l_multifootnote_space_after_comma_dim } \thefootnote }
}
{
\cs_gset:cpx { multifootnote-tag- ##1 } { \thefootnote }
}
}
}
\NewCommandCopy \footnotetag \multifootnotetag
\NewDocumentCommand \multifootnotetagtext { O{} m }
{
\group_begin:
\tl_if_blank:nTF { #1 }
{
\def \thefootnote { }
}
{
\def \thefootnote { \use:c { multifootnote-tag- #1 } }
}
\footnotetext { #2 }
\group_end:
}
% The combined interface for producing footnote text
\NewDocumentCommand \multifootnote { O{} m }
{
\str_if_in:nnTF { #1 } { , }
{
\multifootnotetext [ #1 ] { #2 }
}
{
\multifootnotetagtext [ #1 ] { #2 }
}
}
\endinput
%%
%% End of file `multifootnote.sty'.
MWE 的预期输出应该是这样的: