Created
December 23, 2020 19:40
-
-
Save slowkow/3b0993f2ebb53a5bd0fb9dd4097f8588 to your computer and use it in GitHub Desktop.
Make a montage from a list of PDF files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
montage.py | |
2020-12-23 | |
Kamil Slowikowski | |
Usage | |
----- | |
Write montage.png from a list of PDF files, each panel 500px wide: | |
python montage.py -p 500 file-*.pdf montage.png | |
Requirements | |
------------ | |
This script depends on the following command line tools: | |
- GhostScript | |
- pngquant | |
- ImageMagick (or GraphicsMagick) | |
On macOS, install the dependencies like this: | |
brew install gs pngquant graphicsmagick | |
""" | |
from glob import glob | |
from natsort import natsorted | |
from pathlib import Path | |
from shlex import quote | |
import click | |
import re | |
import subprocess | |
def optimize_png(file): | |
file_fs8 = file.replace(".png", "-fs8.png") | |
cmd = f'pngquant "{file}" && mv -f "{file_fs8}" "{file}"' | |
print(f'Optimizing {file}') | |
return subprocess.call(cmd, shell = True) | |
def pdf_to_png(file, res=200): | |
assert Path(file).is_file() | |
png = file.rstrip('.pdf') + '.png' | |
if not Path(png).is_file(): | |
print(f'Writing {quote(png)}') | |
cmd = f'gs -sDEVICE=pngalpha -o {quote(png)} -r{res} {quote(file)}' | |
p = subprocess.Popen( | |
cmd.split(), | |
stdout = subprocess.PIPE, | |
stderr = subprocess.PIPE, | |
) | |
output, err = p.communicate() | |
assert p.returncode == 0 | |
return png | |
def get_ext(file): | |
m = re.search(r"\.(.+)$", file) | |
if m: | |
return m.group(1) | |
return None | |
@click.command() | |
@click.argument('in_files', nargs = -1) | |
@click.argument('out_file', nargs = 1) | |
@click.option('-p', '--pixels', 'pixels', default = 384, | |
help = 'Width, in pixels, of each panel') | |
@click.option('--optimize/--no-optimize', 'optimize', default = True, | |
help = 'Run pngquant to optimize the output, if it is a PNG') | |
def montage(in_files, out_file, pixels = 384, optimize = True): | |
assert len(in_files) >= 2 | |
in_files = list(in_files) | |
for i in range(len(in_files)): | |
if get_ext(in_files[i]) == 'pdf': | |
in_files[i] = pdf_to_png(in_files[i]) | |
cmd = f"montage {' '.join(in_files)}" | |
cmd += f" -geometry {pixels}x+2+2 {out_file}" | |
print(f'Writing {out_file}') | |
retval = subprocess.call(cmd, shell = True) | |
if optimize and get_ext(out_file) == 'png': | |
retval = optimize_png(out_file) | |
return retval | |
if __name__ == '__main__': | |
montage() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment