Printing a booklet on a Mac
I had a 47-page PDF document that I wanted to turn into a compact A5 booklet - you know, one of those things where you get out the big stapler and make something like this:

Unfortunately, you need to print page 39 on the same page as page 10, and then pages 40 and 9 on the other side, etc, and when you get to anything more than about 4 pages it's hard and tedious to do this by hand and get it right. My HP printer driver had a 'booklet' facility, and it worked OK for 8 pages, but let me down when given any more.
Now, there are utilities you can buy which can do exactly this, but why would I spend a tenner or two when I could instead use a few otherwise lucrative working hours and build one myself?
So here's my solution, which could be a lot tidier, but does the only tricky bit of the job: getting the pages in the right order. It's an Automator script which you can run as a service: once installed, you can right-click on a PDF and select Services, and you should find a 'Make Booklet PDF' option. It produces a new PDF on your Desktop with appropriately shuffled pages. You can then just print that using Preview as follows:
- Go into the Layout section of the Print dialog.
- Select Two-Sided Printing
- Set Pages per Sheet to '2'
- Set the 'Two-Sided' option to 'Short-Edge Binding'
Now, please note: You should understand how this works before trying it! It's not complex, and I could have made it much prettier and self-explanatory, but I was using Automator, which is so far from a real programming language as to be frustrating. It does, however, have a few useful ways of manipulating PDFs without having to install anything else, and my script will at least prompt you with some of the following information when you run it.
(Note too that if you're reading this in 2022 or later, you definitely want to continue on to read the updates at the bottom of the post!)
OK,
First, the number of pages in your starting PDF must be a multiple of 4. Fortunately, you can easily append blank pages in Preview if needed. Select the last page and choose 'Edit > Insert > Blank Page' as often as needed and then save. The script will warn you if your page count isn't right.
Then when you run the script, it will create a folder called 'booklet-pages' on your desktop. In here, it will create one PDF for each page of your document.
Finally, it will work out what order these pages should be in, and create a new 'booklet.pdf' on your desktop with the pages reassembled in that order.
You can then delete the 'booklet-pages' folder.
So, here's a zip file containing the Automator script. You should be able to double-click it and open it in Automator if you want to see what's inside, but I think if you put it into your ~/Library/Services folder within your home directory, it will probably just appear as a service if you right-click on a PDF file in the Finder.
Make Booklet PDF on desktop.zip (for pre-Monterey Macs)
Hope it's useful to someone! Sorry I can't provide any support if you try it, but recommendations and improvements are welcome from anybody with more Automator stamina than me! All I can say is that it works nicely on my Mac running High Sierra (10.13.6).
Some updates: please read these when you've read all of the above
Update:
If you're doing this kind of thing, you may also like a video of MacOS PDF tips and tricks, which I made back in 2016 but which people still say they find useful.
Update April 2022:
MacOS version 12.3 and onwards doesn't include Python 2, on which this script depended, and, while it does include Python 3, Apple haven't updated Automator to make the switch quite as trivial as it should be! However, I've created an updated version which works on my Mac; I hope somebody can confirm that it works for them too! (See below.)
Update June 2022
The new version of the ZIP file below tries harder to find a copy of Python 3 on your computer.
For MacOS 12.3 and later:
- Download this ZIP file: Make booklet PDF on desktop - 2022b.zip.
- Your browser may uncompress this for you; if not, then double-click on the ZIP file to reveal the 'Make booklet PDF on desktop' workflow inside.
- Double-click on this and you should be asked if you want to install it as a 'Quick action'
- This will then be copied into your
~/Library/Servicesfolder as mentioned above, and you should find it in the 'Quick actions' submenu if you right-click on a PDF.
Update Sept 2023
Some people were seeing a final PDF that was blurry. I've changed the way the individual pages are combined back into a single PDF, which should fix this. It works on my Mac running Ventura 13.5.
For MacOS 13 and later:
- Use this download link: Make booklet PDF on desktop -2023.zip
- Double click the file as described above to install.
Comments
Thanks Vladimir - I should have another look at that. I tended to stay clear of Acrobat Reader in the past because of all the other things it wanted to do and the ways it wanted to set itself as the default for lots of things, and I preferred the clean approach of Preview.
But perhaps I should take another look!
All the best, Quentin
Hi Kray - that'll be a setting on your printer driver, I expect.
Do you have an option for long-side binding vs short-side binding? That might do the trick.
Quentin
Hi Debra -
No, I'm afraid this is definitely Mac-only; it wouldn't work on iOS. I haven't done a search to see whether there's anything out there that might: it's not the sort of thing I get the urge to do on my phone, or even my iPad, I must admit!
Best, Quentin
if anyone wants to do this in python
usage, if we have a file
document.pdf: `python3 booklet.py documentHi Christine -
Ah, I'm afraid, at that point, it's dependent on your printer and the printer driver, and how you set up two-sided printing. I put that description in because that's how it looks for most double-sided printers. Your options may be different.
If yours doesn't actually print double-sided, you might have to do something like printing the odd-numbered pages and then feeding them back in to print the even-numbered ones... but that's something you'd need to work out at your end!
Sorry not to be more help! Quentin
Hi Quentin! I needed something a little different--take a series of small pages and make a mini-booklet. Ben Corser's code above gave me the path forward. Below is what I use to take a series of 3" x 4" pages in PDF and produce a PDF I can print, cut, staple, and trim to make my booklets.
# bookleter.py # Given a pdf of a series of pages (width<4, height<11), creates # a new pdf that can be printed double-sided, cut to height, stapled, and trimmed # to make a mini booklet. Pages are centered on the paper. # # If possible, will place a second copy of the pages on bottom half of page, so you # don't have to run the pages through the printer a second time (or waste the paper). # # python3 bookleter.py input.pdf output.pdf # output is 8.5 x 11 size # (can change outputPageWidth & outputPageHeight below) # # dgrover@redcedar.com 11/14/2021 # dgrover 1/7/2022 added 2nd copy if two will fit on a page, use arguments for input/output file names # Based on code by Ben Corser in a comment on Q's blog: https://statusq.org/archives/2019/01/11/8893/ import sys import PyPDF3 as pdf from PyPDF3.pdf import PageObject # get input/output file names if len(sys.argv) < 3: print("Usage: bookleter.py input.pdf output.pdf") exit() inputFileName=sys.argv[1] outputFileName=sys.argv[2] # open our input and output files readFile = pdf.PdfFileReader(open(inputFileName, 'rb')) writeFile = pdf.PdfFileWriter() # create a list of page indices pages = list(range(readFile.getNumPages())) # we do this so we can flag padded pages (==None) # pad page index list with empty pages until its length is a multiple of four # (4 pages for each sheet of the booklet) # Note that when using the index, you must test if a page is None. # If so, then make it blank on output while len(pages) % 4: pages.append(None) # get input document dimensions (in default user space units, 1/72 inch) # Note: coords in user space default to (0,0) in bottom left corner. inputPageWidth = readFile.getPage(0).mediaBox.getWidth() inputPageHeight = readFile.getPage(0).mediaBox.getHeight() # blank input page blackInputPage = PageObject.createBlankPage(None, inputPageWidth, inputPageHeight) # output dimensions, std 8.5x11 outputPageWidth = 72.0 * 8.5 outputPageHeight = 72.0 * 11.0 # will two fit on a single page? (i.e., is orig page height < outputPageHeight/2) if inputPageHeight <= outputPageHeight/2: twoPerPage=True print("Will output two copies per page") else: twoPerPage=False # translation (for top or only) translateY=outputPageHeight - inputPageHeight translateXLeft = (outputPageWidth/2) - inputPageWidth translateXRight = outputPageWidth/2 if twoPerPage: # translation for bottom version, if applicable translateY_bot = 0 translateXLeft_bot = (outputPageWidth/2) - inputPageWidth translateXRight_bot = outputPageWidth/2 # each physical sheet of paper carries 4 pages # Side 0 contains D (left) and A (right) # side 1 contains B (left) and C (right) # and, if two copies of the input can fit on a side of a sheet, # add a second copy at the bottom for sheet in range( int(len(pages)/4)): #print("Sheet {0:d}".format(sheet)) # calculate what pages go where: pageA=sheet * 2 pageB=pageA + 1 pageD=(len(pages)-1) - (sheet*2) pageC=pageD - 1 # side 0 # create blank page (output size) new_page = PageObject.createBlankPage(None, outputPageWidth, outputPageHeight) # add page D (left) if pages[pageD]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageD]), translateXLeft, translateY) # add page A (right) if pages[pageA]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageA]), translateXRight, translateY) # repeat if needed for bottom if twoPerPage==True: if pages[pageD]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageD]), translateXLeft_bot, translateY_bot) # add page A (right) if pages[pageA]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageA]), translateXRight_bot, translateY_bot) # output this page writeFile.addPage(new_page) # side 1 # create blank page (output size) new_page = PageObject.createBlankPage(None, outputPageWidth, outputPageHeight) # add page B (left) if pages[pageB]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageB]), translateXLeft, translateY) # add page C (right) if pages[pageC]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageC]), translateXRight, translateY) # repeat if needed for bottom if twoPerPage==True: # add page B (left) if pages[pageB]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageB]), translateXLeft_bot, translateY_bot) # add page C (right) if pages[pageC]!=None: new_page.mergeTranslatedPage(readFile.getPage(pages[pageC]), translateXRight_bot, translateY_bot) # output this page writeFile.addPage(new_page) writeFile.write((open(outputFileName, 'wb'))) print("Wrote {0:d} double-sided sheets with {1:d} pages total.".format(int(len(pages)/4), len(pages) ))Hi Gerry -
I've added an updated version at the bottom of the description, that should work with the Python3 included in macOS 12.3 and above.
All the best, Quentin
I've now set the PATH in the script, so hopefully it'll have a better chance of finding python3.
Thanks Mattie -
That's what I would have done too, but I don't think people on a standard installation will have a python3 interpreter in /usr/local/bin?
I thought there was more chance they would have one on their PATH, hence my solution, but I may be wrong!
Quentin
Hi Sara -
Are you running a recent version of MacOS? (ie. Monterey (12.3) or later?)
If so, did you download the version in the updates at the bottom of the post? (It might still not work, but certainly won't if you didn't do that!)
Best, Quentin
Hi Dan -
Mmm. I've never seen that; it must be something about the way your printer thinks about double sided pages. I don't suppose the long-edge binding works?
It wouldn't be completely trivial to add a rotation option, so the best I can suggest if your PDF isn't too long is to open it in Preview, Cmd-Click every second page, and then you can rotate all of them by clicking the rotate arrow in the toolbar twice.
Not ideal, I know, but perhaps OK for short documents?
Hi Blanka -
I think you can just go to your ~/Library/Services folder and delete it?
Quentin
Hi Blanka -
OK, try this, which works on my machine running Monterey:
Go to System Preferences and select 'Extensions'
Choose the 'Finder' section on the left.
On the right you should see 'Make booklet PDF on desktop'. You can uncheck it to stop it appearing, but you can also right-click and you'll see 'Show in Finder' and 'Move to bin'.
(On my machine, 'Show in Finder' does indeed show me the file in ~/Library/Services, but yours may be somewhere different.)
Does that help? Quentin
This isn't directly related to my script: it looks as if you may have installed an old version of the developer tools at some point. Try Googling the error message... pages like this might help:
https://apple.stackexchange.com/questions/321858/xcrun-missing-installing-xcode-command-line-tools-not-fixing-issue
Because that's a feature of your particular app or printer driver and isn't an available option for most people.
Q
Not at all - I think you should have Python installed already - I'm not sure how technical your background is, but if you run the Terminal and type in 'python3 --version' it should tell you whether you have it.
In the unlikely event that you don't, you can get it from python.org, and it's only about 40MB, so unless you have the very slowest connection in the world...
Mmm. How very strange. I wonder why your machine needs that...
Well, you could try installing just the command-line tools with
xcode-select --installand see if that works, or you could download and install python directly from python.org ?Great -
The 'unknown option' message was because the formatting on my suggestion had turned two dashes into one long one. It should have been
python3 --version, but when you have it with a single dash, it treats it likepython3 -v -e -r -s -i -o -nand there's no-eoption!Anyway, it looks as if you have a copy of python installed as part of the developer tools now as well as the normal system one which I think is in
/usr/bin... but as long as it's working, that doesn't matter!Best, Q
Hi RF - That's one I haven't heard before, and it doesn't happen for me... (I'm still on Monterey - haven't tried Venture yet.)
A greyed-out booklet.pdf probably means some issue with your permissions to access the file on your desktop. I think the only way that would be likely to happen would be if the script was running as a different user from you, or if your desktop or the underlying filesystem had some slightly unusual configuration.
If you right-click on it and choose
Get Info..., does it tell you anything helpful under theSharing & Permissionssection?I think this should be fixed now - there's a new version at the end of the post which fixes it for me.
Mmm. I haven't seen any other reports of that. Do you have another rather different PDF you can try to see whether it's something about that file or about your system?
I'm not up to date on the various protection mechanisms PDFs (or particular fonts) might be able to employ to prevent copying - perhaps something like that is kicking in?
Q
You don't get anything like this?
Thanks, Stephen - glad it's useful! Quentin
I thought python3 came as standard on macos? Perhaps they've dropped it: which os version are you using?
Ah, interesting. That's a pity if they want that. Though I think it probably only needs the command-line tools, not the whole of Xcode? Don't know how big that would be...
No, I don't really use Xcode any more either. I'm guessing that installing python3 using Homebrew or the installer from Python.org would be much smaller. I'll update the post, though, so that people know.
OK - let me know how you get on!
If you open the automation with Automator (by finding it under ~/Library/Services, and doing an 'Open with...'), you can see the 'Run Shell Script' step which tries to find python3, by using your existing PATH with '/usr/bin:/usr/local/bin:/opt/homebrew/bin' appended to the end.
It may be that your python3 which is triggering the error is earlier on your path, and you could make things work by changing that line to make sure it finds the officially-installed version first.
pages % 4 = 0(this would be truly blank pages, not "This page has been intentionally left blank"). Generate pages in last, first, last-n, first-n, until first-n+1 = last-n (I think that's right). Output to a new file based upon the same name as the original but with-A5Bookletappended to the name.Hi Richard -
Yes, that's basically right. If you grab the download, right-click and do 'Open with... > Automator', you can see the Python code embedded in the script, which generates those sequences (though I started the numbering from zero).
Improvements welcome! The main issue, as you'll have seen from the comments, is that I wrote the core of this in Python, and the version and location of the python executable has changed a few times over the years with the OS release. I wanted to create something that people could just run without having to install any other libraries, but it might have been easier to package it up with py2app or similar!
Given that it's only a very small bit of python in the middle, I could probably rewrite it as a bash script... Or, actually, I see that Automator now has a 'Run JavaScript' action; I should probably switch to that, but I wonder which OS versions have that.
I might try it. Q
Hi Alison -
Ah, thanks for pointing out that item at the bottom of the 'Pages per sheet' - it's easy to miss and I hadn't found it! I wonder when it was added... (Though it sounds as if it has some limitations, too.)
Essentially, the only thing my script does is to rearrange the pages into the right order. The individual pages are still the A4 pages you would have had otherwise, and it's then up to you to print them out double-sided with two pages per sheet... I expect you might have more control as a result... but you might need to employ some more tools.
One thing I tried just now was to (a) print 2 pages per sheet but, under 'Paper Handling', scale to a destination paper size of A5, and select the PDF menu at the bottom to 'Open in Preview'. This gives you a PDF with two small pages within each A5 page. Then (b) print that, but setting the Scale to 100%, which means that you get the two small pages in the middle of the A4 sheet, which I think is what you wanted?
Quentin
Upside down? Now that's one I haven't heard before! Does it do it for all documents, or just some? As you say, it may not matter... but it depends exactly when things got inverted: before or after the re-ordering. The easiest way to check is probably to try it with something short! (Perhaps 8 pages with a number on each?)
One way you can sometimes turn PDFs into more predictable PDFs is to open them in Preview and then do 'File > Print... > PDF' - might be worth a try? (I also use this if I need to convert a PDF with a password into one that doesn't have a password.)
Hi there - I think this is an issue with the configuration of your system, rather than anything specifically to do with this script, but Googling for that error should help! For example:
https://forums.developer.apple.com/forums/thread/711512
All the best, Quentin
Hi Serge -
I did a quick search for the key parts of your error message, and I think it's probably an issue with your Python installation, or perhaps with some environment variables you have set? (PYTHONPATH or PYTHONHOME, perhaps?)
Quentin
The action ""Run Shell Script"" encountered an error: ""xcrun: error: unable to load libxcrun (dlopen(/Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib, 0x0005): tried: '/Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/System/Volumes/Preboot/Cryptexes/OS/Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib' (no such file), '/Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e'))).""========= I'm noting it here in case there's an easy fix for the workflow. Cheers, DylanHi Dylan - thanks for this.
Looks as if you might have XCode or some part of it installed, perhaps for the wrong architecture?
best, Quentin
:-) I love it!