Skip to content

Instantly share code, notes, and snippets.

@slowkow
Created December 23, 2020 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slowkow/3b0993f2ebb53a5bd0fb9dd4097f8588 to your computer and use it in GitHub Desktop.
Save slowkow/3b0993f2ebb53a5bd0fb9dd4097f8588 to your computer and use it in GitHub Desktop.
Make a montage from a list of PDF files
#!/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