这更像是一个假设性问题。它不是要安装和加载已声明但未安装的缺失包,例如 MiKTeX 所做的那样。
假设有人正在编写报告或文章。在输入代码时,例如
\documentclass[a4paper,12pt,onecolumn]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
%\usepackage{lipsum} %<--- forgot to actually load resp. make clear the package is needed. Or was commented out in an earlier session.
\title{a nice title}
\author{John Doe}
\date{\today}
\begin{document}
\maketitle
\chapter{Why the sun is hot}
\section{About the sun}
\lipsum %<--- first call of macro, but it's unknown to the compiler so an error is thrown. the usual behaviour.
\chapter{Why it's a bad idea to touch the sun}
\section{The temperature of the sun}
\lipsum
\end{document}
在这种情况下,包lipsum
被忘记告知。在这种情况下的常见行为:抛出错误。
这种行为导致作者修复问题并重新开始编译,所有这些都很耗时。
现在:如何才能让编译器在第一次解析关键字时(在我们的示例中:)方便地加载缺少的包,\lipsum
并继续编译而不会引发错误并中断编译文档?
答案1
由于这个问题已经开放了一个月,我想我会尝试一下。从问题中的描述来看:
这种行为导致作者修复问题并重新开始编译,所有这些都很耗时。
我认为真正的问题是关于人类必须付出的代价手动修复并重新编译。(“编译”仅指用户可见的行为,即调用某个程序 — 例如latexmk
或arara
— 而不一定是 pdfTeX(或任何)程序本身。)根据这种解释,原则上绝对可以为用户节省一些时间。
(当然,只要你愿意改变 *TeX 程序本身,一切皆有可能,但无论好坏,在 TeX 世界中这样的事并不多。)
这个答案的底部是一个概念验证 Python 脚本。假设你把它放入一个名为的文件中invoke.py
。然后pdflatex bad.tex
你可以执行python3 invoke.py bad.tex
,你会在终端中看到以下输出:
The control sequence \lipsum was undefined
Adding package lipsum to the file.
Great, nothing was undefined this time. This was the output:
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./bad.tex
LaTeX2e <2017-04-15>
Babel <3.10> and hyphenation patterns for 84 language(s) loaded.
(/usr/local/texlive/2017/texmf-dist/tex/latex/base/article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(/usr/local/texlive/2017/texmf-dist/tex/latex/base/size10.clo)) (/usr/local/texlive/2017/texmf-dist/tex/latex/lipsum/lipsum.sty) (./bad.aux) [1{/usr/local/texlive/2017/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] [2] [3] (./bad.aux) )</usr/local/texlive/2017/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb></usr/local/texlive/2017/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on bad.pdf (3 pages, 36350 bytes).
Transcript written on bad.log.
前三行来自 Python 脚本。运行此脚本后,文件bad.tex
将更改为包含\usepackage{lipsum}
其中的行。由于整个过程需要 0.47 秒(而且现在 TeX 总体来说非常快),我认为它真正解决了问题(允许用户避免手动将包添加到文件的耗时步骤),即使在幕后,TeX 程序已被调用多次。(实际上,即使 TeX 需要很长时间,事实是程序可以比人类更快地添加缺失的包,而人类无论如何都必须重新运行该程序,所以它总是更快。)
这是 Python 脚本invoke.py
:
import subprocess
import sys
giant_map_of_macros_to_packages = {
'lipsum': 'lipsum',
# ...
}
def add_package(package, filename):
"""Inserts a usepackage line into the file."""
with open(filename) as f:
contents = f.read()
where = contents.find('\\begin{document}')
with open(filename, 'w') as f:
f.write(contents[:where])
f.write('\\usepackage{%s}\n' % package)
f.write(contents[where:])
if __name__ == '__main__':
filename = sys.argv[1]
run_again = True
while run_again:
run_again = False
completed = subprocess.run(['pdflatex', '-halt-on-error', filename],
stdout=subprocess.PIPE)
lines = completed.stdout.decode('utf-8').splitlines()
try:
where = lines.index('! Undefined control sequence.')
line = lines[where + 1]
undefined = line[line.rfind('\\') + 1:]
print('The control sequence \\%s was undefined' % undefined)
package = giant_map_of_macros_to_packages[undefined]
print('Adding package %s to the file' % package)
add_package(package, filename)
run_again = True
except ValueError:
print('Great, nothing was undefined this time. This was the output:')
print('\n'.join(lines))
except KeyError:
print('Not sure what package defines %s. Add it to giant_map_of_macros_to_packages' % undefined)
明显地:
- 这只是一个概念证明(表明它是可能的);正确的位置可能是标准 LaTeX 构建工具之一,例如
latexmk
或arara
。 - [在我看来,更好的办法是将这样的功能添加到 TeX 程序本身中,但这永远不会发生。]
\begin{document}
这假设了一些事情(通常是正确的),例如文件中存在一行,在该行之前可以安全地插入\usepackage{...}
一行,未定义的宏是消息\\
后面行的最后一行之后的所有内容! Undefined control sequence.
,等等。- 有人必须维护
giant_map_of_macros_to_packages
— 尽管原则上这也是 TeX 内部可自动化的。(例如使用 LuaTeX:对于每个包,比较加载包之前和之后的宏哈希表。)当多个包定义相同的宏时,优雅地处理这种情况也是很好的。