lastpage实现原理及简单复现

发布于 2022-06-20 14:13:10

lastpage

lastpage 包用于获取到文档的总页数,其原理是通过 LaTeX 提供的 hook \AtEndDocumnet 命令,在 \end{document} 之前去获取最后一页的页码计数器的值,并保存在一个命令里面,然后前文使用这个命令就能得到这个值


大致思路是这样,我们可以在导言区写道:

\AtEndDocument{
  \def\lastpage{\thepage}
}

然后在前文使用 \lastpage, 很遗憾,会报错(

  1. 报错内容是 command lastpage undefined, 因为 tex 程序是从前往后运行的,也就是说,\lastpage 定义在后,使用在前. 解决这个问题,我们需要将定义内容写到外部文件中,然后在导言区 \input 这个文件.
  2. 即使上一个问题可能得到解决了,还是会有问题
  • 因为即使我们在外部文件中写道 \def\lastpage{\thepage} 然后在导言区引入,无异于直接在导言区写道 \def\lastpage{\thepage}, 也就是说你在文档中写下 \lastpage ,无异于直接写 \thepage,并不会得到最后一页的页码
  • 解决它的办法是将 \def 改为 \xdef,区别在于 \xedf会将 \thepage 展开,而 \def 只是简单的宏替换

完整 MWE

\documentclass{article}
\ExplSyntaxOn
\iow_new:N \l_lastpage_file 
\file_if_exist:nTF{\c_sys_jobname_str.page}
{
  \file_input:n{\c_sys_jobname_str.page}
}
{
  \cs_set_eq:NN \lastpage \relax
}
\AtEndDocument{
\iow_open:Nn \l_lastpage_file{\c_sys_jobname_str.page}
\iow_now:Nx \l_lastpage_file 
{
  \cs_set:Npn \exp_not:N \lastpage 
  {
    \thepage
  }
}
\iow_close:N \l_lastpage_file 
}
\ExplSyntaxOff

\begin{document}
aaa \lastpage
\newpage
bbb 
\newpage
ccc 
\newpage
ddd 
\end{document}

原谅我使用了 LaTeX3,代码后面再来解读,先去上课了...


代码解读

注意到下面的代码

\iow_new:N \l_lastpage_file 
\AtEndDocument{
\iow_open:Nn \l_lastpage_file{\c_sys_jobname_str.page}
\iow_now:Nx \l_lastpage_file 
{
  \cs_set:Npn \exp_not:N \lastpage 
  {
    \thepage
  }
}
\iow_close:N \l_lastpage_file 
}

第一行 \iow_new:N \l_lastpage_file 是新建一个文件写入流,第三行 \iow_open:Nn \l_lastpage_file{\c_sys_jobname_str.page} 是打开这个写入流,并写入 <当前项目名.page> 这个文件中.

 \iow_now:Nx \l_lastpage_file 
{
  \cs_set:Npn \exp_not:N \lastpage 
  {
    \thepage
  }
}

用于在文件中写入内容. 写入的内容相当于前文中提到的 \xdef\lastpage{\thepage}.
\iow_close:N \l_lastpage_file 用于关闭这个写入流.
运行后文件夹中就会出现一个 xxx.page 的文件,内容是:

\cs_set:Npn \lastpage {<最后一页的页码值>} %相当于 \xdef\lastpage{<最后一页的页码值>}

如果我们直接在导言区写道 \input{xxx.page} 会有问题,因为第一次编译的时候 LaTeX 运行到该行的时候,该文件还没有生成. 自然会报错,文件没写入, 也意味着 \lastpage命令还没定义.
解决这个问题如下写法:

\file_if_exist:nTF{\c_sys_jobname_str.page}
{
  \file_input:n{\c_sys_jobname_str.page}
}
{
  \cs_set_eq:NN \lastpage \relax
}

判断一下文件是否存在,如果存在就导入,不存在就先初始化一下 \lastpage 命令,避免出现未定义的报错.

0 条评论

发布
问题