由于误用 \l_tmpa_clist 导致 TeX 容量超出

由于误用 \l_tmpa_clist 导致 TeX 容量超出

我正在尝试改进我的套餐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 的预期输出应该是这样的:

在此处输入图片描述

相关内容