使用Python脚本生成PDF-LaTeX

34

我是一名大学生,在我的学校,无论何种类型的作业都需要标准的封面(包括学校标志、课程名称、教授姓名、我的姓名等等)。

因此,我有一个.tex文档,用于生成我的标准封面的pdf文件。它大致如下:

...
\begin{document}
%% College logo
\vspace{5cm}
\begin{center}
\textbf{\huge "School and Program Name" \\}
\vspace{1cm}
\textbf{\Large "Homework Title" \\}
\vspace{1cm}
\textbf{\Large "Course Name" \\}
\end{center}
\vspace{2.5cm}
\begin{flushright}
{\large "My name" }
\end{flushright}
...

我在想是否有一种方法可以编写一个Python脚本,询问我作业的标题、课程名称和其他字符串,并使用它们来生成封面页。之后,它应该编译.tex文件并使用给定的信息生成pdf。

欢迎提供任何意见、建议、代码片段或库。


1
文本编辑器更快,我个人认为。每次打印封面时仍需要输入标题、课程名称等信息。我会将PDF转换为Word模板。可以尝试使用:http://www.pdftoword.com/ - Alvin K.
1
你可以在文本编辑器中使用代码片段管理器/模板系统。 - N.N.
3
好主意!但我在寻找更多自己制作/极客风格的东西。 - juliomalegria
4个回答

79
您可以从将模板文本文件定义为字符串开始:
content = r'''\documentclass{article}
\begin{document}
...
\textbf{\huge %(school)s \\}
\vspace{1cm}
\textbf{\Large %(title)s \\}
...
\end{document}
'''

接下来,使用argparse来接受课程、标题、姓名和学校的值:

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--course')
parser.add_argument('-t', '--title')
parser.add_argument('-n', '--name',) 
parser.add_argument('-s', '--school', default='My U')

只需要进行一点字符串格式化就可以将 args 嵌入到 content 中:

args = parser.parse_args()
content%args.__dict__

将内容写入文件cover.tex后,

with open('cover.tex','w') as f:
    f.write(content%args.__dict__)

您可以使用subprocess调用pdflatex cover.tex

proc = subprocess.Popen(['pdflatex', 'cover.tex'])
proc.communicate()

您可以在这里添加一个 lpr 命令,以将打印添加到工作流程中。
删除不必要的文件:
os.unlink('cover.tex')
os.unlink('cover.log')

脚本可以这样调用:
make_cover.py -c "Hardest Class Ever" -t "Theoretical Theory" -n Me

将所有内容结合在一起,
import argparse
import os
import subprocess

content = r'''\documentclass{article}
\begin{document}
... P \& B 
\textbf{\huge %(school)s \\}
\vspace{1cm}
\textbf{\Large %(title)s \\}
...
\end{document}
'''

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--course')
parser.add_argument('-t', '--title')
parser.add_argument('-n', '--name',) 
parser.add_argument('-s', '--school', default='My U')

args = parser.parse_args()

with open('cover.tex','w') as f:
    f.write(content%args.__dict__)

cmd = ['pdflatex', '-interaction', 'nonstopmode', 'cover.tex']
proc = subprocess.Popen(cmd)
proc.communicate()

retcode = proc.returncode
if not retcode == 0:
    os.unlink('cover.pdf')
    raise ValueError('Error {} executing command: {}'.format(retcode, ' '.join(cmd))) 

os.unlink('cover.tex')
os.unlink('cover.log')

6
非常棒的解决方案,而且很简单!现在我有一个自动的封面生成器 :) - juliomalegria
有没有一种方法可以检查PDF是否成功生成?我发现如果文本中有“&”或“%”,它会破坏PDF。 - Kritz
1
@Johan:该脚本显示对pdlatex的调用输出。如果处理LaTeX时出现错误,这些错误消息将向您显示pdf未成功生成。在Python中,&不是特殊字符,但在TeX中是,因此如果要使用字面上的“&”,则需要在其前加上反斜杠:\&是Python和TeX中的特殊字符。根据所在的位置,可能需要将其更改为\%%% - unutbu
谢谢Unutbu。实际上我想在远程服务器上运行这个程序,所以我无法查看输出结果。现在我所做的就是检查PDF文件是否已生成。如果已生成,我就认为一切正常并发送PDF文件;如果没有生成,服务器将会返回一个错误信息。您有更好的建议吗? - Kritz
或者,当 retcode != 0 时,您可以调用 os.unlink(cover.pdf) - unutbu
显示剩余4条评论

7
当然可以使用像Jinja这样的模板系统,但对于您要求的内容来说,它们可能过度了。 您还可以使用RST格式化页面,并使用它来生成LaTeX,但同样也可能过度了。 哪怕是根据定义的数量自动生成页面也可能过度,但既然我们有了这个选项,为什么不用呢! :)
我已经使用Python的字符串格式化做过类似的事情。使用“占位符名称1”标记将您的LaTeX文档进行“标记化”,并将%(placeholder_name1)s占位符放入文档中。例如,当您想要输入班级名称时,请使用%(course_name)s
\textbf{\Large "%(homework_title)s" \\}
\vspace{1cm}
\textbf{\Large "%(course_name)s" \\}

接着,从Python中,你可以加载该模板并按如下格式进行格式化:

template = file('template.tex', 'r').read()
page = template % {'course_name' : 'Computer Science 500', 
                   'homework_title' : 'NP-Complete'}
file('result.tex', 'w').write(page)

如果您想自动查找这些令牌,以下内容应该可以很好地完成:
import sys
import re
import subprocess

template = file('template.tex', 'r').read()
pattern = re.compile('%\(([^}]+)\)[bcdeEfFgGnosxX%]')
tokens = pattern.findall(template)

token_values = dict()
for token in tokens:
    sys.stdout.write('Enter value for ' + token + ': ')
    token_values[token] = sys.stdin.readline().strip()

page = template % token_values
file('result.tex', 'w').write(page)

subprocess.call('pdflatex result.tex')

该代码将遍历标记并在控制台上打印提示,要求您为每个标记输入内容。在上面的示例中,您将获得两个提示(附带示例答案):
Enter value for homework_title: NP-Complete
Enter value for course_name: Computer Science 500

最后一行调用pdflatex命令处理生成的文件并转换为PDF文档。如果您需要更多功能,还可以要求用户输入输出文件名或将其作为命令行选项。

我需要在 subprocess 调用中添加 shell=True - TimP
既然我们喜欢过度,我想看看Jinja的答案! - sleblanc

5

还有一个模板类(自2.4起)允许使用$that标记而非%(thi)s


5

有一个专门用于此目的的Python库:PyLaTeX。以下代码是直接从文档中提取的:

from pylatex import Document, Section, Subsection, Command
from pylatex.utils import italic, NoEscape


def fill_document(doc):
    """Add a section, a subsection and some text to the document.

    :param doc: the document
    :type doc: :class:`pylatex.document.Document` instance
    """
    with doc.create(Section('A section')):
        doc.append('Some regular text and some ')
        doc.append(italic('italic text. '))

        with doc.create(Subsection('A subsection')):
            doc.append('Also some crazy characters: $&#{}')


if __name__ == '__main__':
    # Basic document
    doc = Document('basic')
    fill_document(doc)

    doc.generate_pdf(clean_tex=False)
    doc.generate_tex()

    # Document with `\maketitle` command activated
    doc = Document()

    doc.preamble.append(Command('title', 'Awesome Title'))
    doc.preamble.append(Command('author', 'Anonymous author'))
    doc.preamble.append(Command('date', NoEscape(r'\today')))
    doc.append(NoEscape(r'\maketitle'))

    fill_document(doc)

    doc.generate_pdf('basic_maketitle', clean_tex=False)

    # Add stuff to the document
    with doc.create(Section('A second section')):
        doc.append('Some text.')

    doc.generate_pdf('basic_maketitle2', clean_tex=False)
    tex = doc.dumps()  # The document as string in LaTeX syntax

它特别适用于生成自动报告或幻灯片。


2
请注意,像 generate_pdf 这样的东西需要安装 pdflatex 或类似的软件(这些软件依赖于 Perl 吧?)。最终我在 PythonAnywhere 上使用了 PyLaTeX,因为它是免费的,并且几乎默认都可以工作。 - Rusca8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接