[ANN] PDFmerger.jl: produce multiple plots on separate pages in one PDF file

Hi all,

I’m happy to announce the little PDFmerger.jl package to merge PDF (Portable Document Format) files. It is tested on Linux, OS X, and Windows and does not require (manual) installation of external tools.

Besides simple installation, it provides a convenient way to produce a PDF file containing multiple plots on different pages. This topic came up before and there is an open issue for Plots.jl.

using Plots
using PDFmerger: append_pdf!

for t in 1:5
    p = plot(rand(33), title="$t")
    savefig(p, "temp.pdf")
    append_pdf!("allplots.pdf", "temp.pdf", cleanup=true)
end

This produces allplots.pdf that contains all five plots and deletes the temporary file. (Has anybody an idea how to avoid to manually specify a temporary file name? A clever macro maybe?)

I hope that may be helpful! I’m open to any suggestions :slight_smile:

Best,
Andreas

19 Likes

Does it work with Makie.jl?

1 Like

If you save the Makie figure as PDF (with CairoMakie) it should work.

PDFmerger does only handle PDF files, so it doesn’t know (or care) if the files where produced by Makie, Plots.jl, or anything else.

3 Likes

That could be very helpful, thanks !
A solution without macro:

function with_merge_pdfs(inner, output, iterables...)
	empty!(output)  # to replace with rm(output_path) in final version
	map(iterables...) do args...
		mktemp() do path, io
			inner(output, path, args...)
		end
	end
	output
end

As an example involving only arrays, no file (just for testing)

let
	output = []  # would be replaced with output = "output.pdf"
	with_merge_pdfs(output, 1:3) do outp, temp, x
			push!(outp, (temp, x))  # would be replaced with the plot commands
	end
	output
end

ouputs
Screenshot_20220118_163126

The final version would be used as

with_merge_pdfs("allplots.pdf", 1:5) do all, temp, t
    p = plot(rand(33), title="$t")
    savefig(p, temp)
    append_pdf!(all, temp)
end

To be checked, as I’m still learning :slight_smile:

On the matplotlib page for savefig it says:

savefig(fname, …

Parameters

fname str or path-like or binary file-like

A path, or a Python file-like object.

A file-like-object is one which supports the .read() and .write() interface.

Suggesting one could put together an object which somehow uses IOBuffer rather than files.

Very useful! Let’s see how it develops.