Skip to content

Instantly share code, notes, and snippets.

@jpertino
Created January 8, 2011 04:30
Show Gist options
  • Save jpertino/770544 to your computer and use it in GitHub Desktop.
Save jpertino/770544 to your computer and use it in GitHub Desktop.
sort pdf pages for book-like two-page print
@GrabResolver(name='itextpdf', root='http://maven.itextpdf.com/')
@Grab('com.itextpdf:itextpdf:5.0.5')
import static com.itextpdf.text.PageSize.A4
import com.itextpdf.text.Document
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.PdfWriter
def cli = new CliBuilder().with {
usage = 'preparePdf [options] <source file path>'
s args:1, longOpt: 'select', 'Select pages (e.g. -s "1,X,4..7,2,3,X,8..10")'
t args:1, longOpt: 'target', 'Target path'
parse(args)
}
assert cli.arguments().size() == 1 && cli.arguments()[0].endsWith('.pdf')
def sourcePath = cli.arguments()[0]
def targetPath = cli.t ?: sourcePath.replace('.pdf', '-sorted.pdf')
def reader = new PdfReader(new File(sourcePath).newInputStream())
// if no -s selection use full document
def selected = parseSelection(cli.s) ?: [*1..reader.numberOfPages]
// we need to fill missing pages to be divisible by 4
// 'X' is a blank page btw
selected += ['X'] * (-selected.size() & 3)
def doc = new Document(A4, 0, 0, 0, 0)
def writer = PdfWriter.getInstance(doc, new File(targetPath).newOutputStream())
doc.open()
sort(selected).eachWithIndex {it, i->
if (i) doc.newPage()
if (it.is("X")) {
writer.@pdf.@pageEmpty = false // hacky but works
doc.newPage()
} else {
def page = writer.getImportedPage(reader, it)
// find the most appropiate scale and offsets for the page
float scale = [A4.width / page.width, A4.height / page.height].min()
float shiftright = (A4.width - page.width * scale) / 2
float shiftup = (A4.height - page.height * scale) / 2
writer.directContent.addTemplate page, scale, 0f, 0f, scale, shiftright, shiftup
}
}
doc.close()
writer.close()
def parseSelection(input) {
if (input) {
def tokens = input.tokenize(',').collect{it.tokenize('..')}
assert tokens.every{it.size() in [1,2] && it.every{it.integer || it.is('X')}}
tokens.collect {
it.size() == 2? (it[0] as int)..(it[1] as int) :
it[0].is('X')? 'X' : it[0] as int
}.flatten()
}
}
def sort(pages) {
// sort pages for those that don't have a smart printer
pages[(0..<(pages.size()>>1)).step(2).sum {
[pages.size() - it - 1, it, it + 1, pages.size() - it - 2]}]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment