不久前,我回答了一个问题,代码试图设置使用 Ti 生成的外部图像的名称钾Z 的external
库,根据输入文件的名称进行替换。该答案按时间顺序排列,可在以下位置找到https://tex.stackexchange.com/a/334290/。
首先,我提供了一个宏来明确执行此操作。
\extinput{<filename>}
用于<filename>
为外部图像创建名称并读取文件作为输入。
其次,我提供了一个基于的自动化解决方案standalone
。它通过修补sa@document
环境来获取输入文件的当前名称。然后使用它来为外部化图像创建一个名称。
这似乎出奇地有效,我把它变成了 Ti钾我本人一直使用 Z 库,并且一直在使用它。
然而,\extinput{<filename>}
不再起作用。原来这是因为它使用了
\file_input:n
而不是简单地
\input
这些之间有什么相关区别以及哪些变化导致我的代码被破坏?
这花了我一些时间才找到原因,因为我最初认为自动化解决方案肯定行不通,而显式宏更安全。(而且,事实上是自动化版本出了问题。然而,根本原因似乎是一样的。)
\begin{filecontents}{pic.tex}
\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
\path [fill] circle (1);
\end{tikzpicture}
\end{document}
\end{filecontents}
\documentclass{article}
\usepackage{xparse,tikz,standalone,currfile,etoolbox}
\usetikzlibrary{external}
\ExplSyntaxOn
\makeatletter
\tl_new:N \g_enext_figurename_tl
\cs_new_protected_nopar:Nn \enext_settofilename:
{
\enext_tikzsetfigurename:x { \currfilebase- }
}
\cs_new_protected_nopar:Nn \enext_tikzsetfigurename:n
{
\tikzsetfigurename { #1 }
}
\cs_generate_variant:Nn \enext_tikzsetfigurename:n { x }
\cs_new_protected_nopar:Nn \enext_tikzsetnextfilename:n
{
\tikzsetnextfilename { #1 }
}
\cs_generate_variant:Nn \enext_tikzsetnextfilename:n { V }
\cs_new_protected_nopar:Nn \enext_setfigurename:
{
\enext_settofilename:
\enext_tikzsetnextfilename:V \g_enext_figurename_tl
}
\cs_new_protected_nopar:Nn \enext_setfigurename:n
{
\enext_tikzsetfigurename:n { #1 }
\enext_tikzsetnextfilename:V \g_enext_figurename_tl
}
\msg_new:nnnn { enext } { append ~ failed }
{
\msg_warning_text:n { enext } ~ :: ~ Append ~ to ~ #1 ~ failed ~ \msg_line_context: !
}
{
This ~ is ~ probably ~ not ~ your ~ fault ~ unless ~ you ~ redefined ~ the ~ modified ~ functions. ~
Either ~ fix ~ the ~ code ~ yourself ~ or ~ ask ~ for ~ help. ~
Apologies ~ for ~ any ~ inconvenience. ~
All ~ code ~ provided ~ as-is ~ for ~ use ~ at ~ your ~ own ~ risk! ~
As ~ a ~ goodwill ~ gesture, ~ a ~ full ~ refund ~ will ~ be ~ provided ~ on ~ request, ~ less ~ a ~ small ~ fee ~ to ~ cover ~ administration.
}
\msg_new:nnn { enext } { append ~ succeeded }
{
\msg_warning_text:n { enext } ~ :: ~ Modified ~ #1 ~ \msg_line_context: !
}
\NewDocumentCommand \extinput { m }
{
\group_begin:
\enext_setfigurename:n { #1 - }
\file_input:n { #1 }
% \input{#1} % using \input rather than \file_input:n works
\group_end:
}
\apptocmd \sa@document
{
\enext_setfigurename:
}{
\msg_warning:nnn { enext } { append ~ succeeded } { document ~ environment }
}{
\msg_warning:nnn { enext } { append ~ failed } { document ~ environment }
}
\apptocmd \tikzsetnextfilename
{
\tl_gset:Nn \g_enext_figurename_tl { #1 }
}{
\msg_warning:nnn { enext } { append ~ succeeded } { \tikzsetnextfilename }
}{
\msg_warning:nnn { enext } { append ~ failed } { \tikzsetnextfilename }
}
\apptocmd \tikzexternal@getnextfilename@resetglobals
{
\tl_gclear:N \g_enext_figurename_tl
}{
\msg_warning:nnn { enext } { append ~ succeeded } { \tikzexternal@getnextfilename@resetglobals }
}{
\msg_warning:nnn { enext } { append ~ failed } { \tikzexternal@getnextfilename@resetglobals }
}
\makeatother
\ExplSyntaxOff
\tikzexternalize
\begin{document}
\tikzexternalenable
\begin{figure}
\extinput{pic}
\end{figure}
\end{document}
似乎发生的情况是,外部化图形的名称从未正确设置,并且一些不应该优化的东西被优化掉了。
\g__file_internal_ior=\read2
(./<tikz picture filename>.tex
A tikzpicture has been optimized away. Use '/tikz/external/optimize=false' to disable this.
)
这是来自外部化运行的日志,<main filename>-0.log
。该文件甚至不应该存在,因为它应该使用<tikz picture filename>
而不是<main filename>
创建名称。在本例中,它应该使用pic
该名称,因此日志应该是pic-0.log
。
我认为它之所以对其进行优化正是因为名称不正确。
! Package tikz Error: Sorry, image externalization failed: the resulting image
was EMPTY. I tried to externalize '<main filename>-0', but it seems there is no such image in the document!?
如果我注释掉带有 的行\file_input:n
并取消注释带有 的行\input
,那么图像就会按预期写入,pic-0.pdf
日志也会pic-0.log
按预期写入 。
这两种情况之间的相关区别是什么以及/或者还有哪些变化导致了这个问题?
我怀疑\currfilebase
在两种情况下的设置不同,或者在一种情况下没有设置,但我不确定,也不知道原因。如果我注释掉的补丁,它就可以工作了。那么现在和sa@document
之间是否存在冲突?\currfilebase
\file_input:n
编辑
我认为问题在于不再像以前那样\file_input:n
触发包中的钩子,并且不会像以前那样。因此,这些钩子存储的当前文件信息不再通过它提供的宏获得,而这些宏依赖于数据。filehook
\input
currfile
我知道我可以使用以下方法检索当前文件的名称
\g_file_current_name_tl
但是,我需要处理它以提取基本名称。显然这是可以做到的,但要稳健/安全地做到这一点似乎并不是一件容易的事。我可以通过其他方式获取此信息吗?
答案1
方法是expl3
,每个命令都有明确、定义的语义,独立于其他加载的代码。相反,LaTeX 文档命令\input
(如问题中观察到的)由filehooks
包修改。因此,\file_input:n
它更像是 TeX\input
原语(\@@input
在 LaTeX 中),而不是文档级\input
命令。
在 中expl3
,\file_input:n
是一个相对低级的函数,用于此时读取指定文件的内容,并对操作进行一些“整理”。如前所述,它确实设置了\g_file_current_name_tl
(尽管目前该领域仍有一些问题需要解决)。如果需要提供其他数据(可能是“当前基本名称”),则向 LaTeX-L 列表或GitHub 问题跟踪器是合适的。