Search code examples
rbashcatpdftk

How to create repaginating sequences for booklet printing as output for pdftk?


I'm creating a workflow for booklet printing with 2 pages on an open spread:

  1. Use R to create the page sequence
  2. Use pdftk to repaginate the pdf
  3. Use lp to print the 2-up booklet

I've written a loop for repaginating pdfs for booklet/saddle stitch sequence. For an 8-pg booklet, the print sequence should be "8 1 2 7 6 3 4 5". I can create the sequence in the following loop but don't know how to output it into a single line with each page number separated with a space.

p <- 16  # number of pages in a saddle stitch, multiple of 4
p.seq <- c(1:p) # page sequence
p2 <- p/2

for (i in 1:p2) {
  ifelse(p.seq[i] %% 2 == 0, # true if even number
    print(paste(i, p - i + 1, sep=" ")),
    print(paste(p - i + 1, i, sep=" "))
    )
}   

Tried to use cat(..., append=TRUE) instead of print, but that stops the loop.


Solution

  • Try this

    p <- 16
    for (i in seq_len(p/2)) {
      if (i %% 2) cat(c(p - i + 1, i), '')
      else cat(c(i, p - i + 1), '')
    }   
    # 16 1 2 15 14 3 4 13 12 5 6 11 10 7 8 9 
    

    If you want to be able to save the result (i.e. not just print/cated on the console), you could write a function f:

    f <- \(p) unlist(lapply(seq_len(p/2), \(i) {
      if (i %% 2) c(p - i + 1, i)
      else c(i, p - i + 1)
    }))
    

    You could use it in a bash script then

    #!/bin/bash
    p=8
    
    o=$(Rscript -e "
    f <- \(p) unlist(lapply(seq_len(p/2), \(i) {
      if (i %% 2) c(p - i + 1, i)
      else c(i, p - i + 1)
    }))
    f($p)
    ")
    
    echo $o  ## use in pdftk instead of echo
    

    $ ./foo.sh
    [1] 8 1 2 7 6 3 4 5
    

    A more fail-safe version of the R function could be

    f <- \(p) {
      if (is.na(p) || is.null(p)) NULL
      else if (p < 0 || p != round(p)) stop('p not a positive round number')
      else if (p == 1) 1
      else {
        unlist(lapply(seq_len(p/2), \(i) {
          if (i %% 2) c(p - i + 1, i)
          else c(i, p - i + 1)
        }))
      }
    }
        
    f(8)
    # [1] 8 1 2 7 6 3 4 5
    
    ## `cat`ed
    cat(f(8))
    # 8 1 2 7 6 3 4 5
    
    f(16)
    # [1] 16  1  2 15 14  3  4 13 12  5  6 11 10  7  8  9
    
    f(1)
    # [1] 1
    
    f(0); f(NA); f(NULL)
    # NULL
    
    f(-3.1); f(-1)
    # Error in f(-3.1) : `p` not a positive round number
    

    Alternatively, to store a for loop result, you could initialize a list res that you can unlist afterwards.

    p <- 8
    res <- vector(mode='list', length=p/2)
    for (i in seq_len(p/2)) {
      if (i %% 2) res[[i]] <- c(p - i + 1, i)
      else res[[i]] <- c(i, p - i + 1)
    }   
    unlist(res)
    # [1] 8 1 2 7 6 3 4 5
    

    to cat it:

    cat(unlist(res))
    # 8 1 2 7 6 3 4 5