# Copyright (C) 2016-2024 Jan Berges
# This program is free software under the terms of the BSD Zero Clause License.
"""Typeset TeX, rasterize PDF."""
from __future__ import division
import os
import subprocess
from .png import save, load
[docs]
def goto(filename):
"""Go to output directory for plot typesetting.
Parameters
----------
filename : str
LaTeX file name including possible path.
Returns
-------
stem : str
File name without path and extension.
typ : str
Desired file type (extension).
home : function
Function to return to previous working directory.
"""
head, tail = os.path.split(filename)
for extension in 'tex', 'pdf', 'png':
if tail.endswith('.%s' % extension):
stem = tail[:-len(extension) - 1]
typ = extension
break
else:
stem = tail
typ = None
if head:
cwd = os.getcwd()
subprocess.call(['mkdir', '-p', head])
os.chdir(head)
def home():
if head:
os.chdir(cwd)
return stem, typ, home
[docs]
def typeset(stem, engine='pdflatex'):
"""Run typesetting engine and remove ``.aux`` and ``.log`` files.
Parameters
----------
stem : str
File name without path and extension (in current working directory).
engine : str, default 'pdflatex'
TeX typesetting engine.
"""
try:
subprocess.call([engine, '--interaction=batchmode', '%s.tex' % stem])
for suffix in 'aux', 'log':
os.remove('%s.%s' % (stem, suffix))
except OSError:
print('Could not typeset %s' % stem)
[docs]
def rasterize(stem, dpi=300.0, width=0, height=0, rewrite=False):
"""Run ``pdftoppm`` and remove ``-1`` from name of resulting PNG file.
Parameters
----------
stem : str
File name without path and extension (in current working directory).
dpi : float, default 300.0
Image resolution in dots per inch.
width, height : int, default 0
Image dimensions in pixels. If either `width` or `height` is zero, it
will be determined by the aspect ratio of the image. If both are zero,
they will also be determined by `dpi`.
rewrite : bool, default False
Rewrite resulting PNG file using StoryLines? This will remove possible
metadata and may reduce the file size but currently is quite slow.
"""
try:
args = ['-png']
if width or height:
args += ['-scale-to-x', '%g' % (width or -1)]
args += ['-scale-to-y', '%g' % (height or -1)]
else:
args += ['-r', '%g' % dpi]
subprocess.call(['pdftoppm'] + args + ['%s.pdf' % stem, stem])
os.rename('%s-1.png' % stem, '%s.png' % stem)
if rewrite:
save('%s.png' % stem, load('%s.png' % stem))
except OSError:
print('Could not rasterize %s' % stem)
[docs]
def combine(filename, pdfs, columns=100, align=0.5, halign='left', pdf=False,
png=False, dpi=300.0):
"""Arrange multiple PDFs in single file.
Parameters
----------
filename : str
Name of combined file.
pdfs : list of str
Names of PDFs to be combined.
columns : int, default 100
Number of PDFs to be arranged side by side.
align : float, default 0.5
Vertical alignment of PDFs, where 0, 0.5, and 1 stand for the bottom,
center, and top of the individual figures.
halign : str, default 'left'
Horizontal alignment of PDFs. Possible values are ``'left'``,
``'center'`` and ``'right'``.
pdf : bool, default False
Convert resulting TeX file to PDF? Automatically set to ``True`` if
`filename` ends with ``.pdf``.
png : bool, default False
Convert resulting PDF file to PNG? This implies `pdf`. Automatically set
to ``True`` if `filename` ends with ``.png``.
dpi : float, default 300.0
Image resolution in dots per inch.
"""
stem, typ, home = goto(filename)
png = png or typ == 'png'
pdf = pdf or typ == 'pdf' or png
with open('%s.tex' % stem, 'w') as tex:
tex.write('\\documentclass[varwidth=1189mm]{standalone}\n'
'\\usepackage{graphicx}\n'
'\\begin{document}\n'
'\\noindent%\n')
if halign == 'center':
tex.write('\\centering%\n')
elif halign == 'right':
tex.write('\\raggedleft%\n')
for n in range(len(pdfs)):
nobreak = (n + 1) % columns or n + 1 == len(pdfs)
tex.write('\\raisebox{-%g\\height}{\\includegraphics{{%s}.pdf}}%s\n'
% (align, pdfs[n], '%' if nobreak else '\\\\[-\\lineskip]'))
tex.write('\\end{document}\n')
if pdf:
typeset(stem)
if png:
rasterize(stem, dpi)
home()