Michael Weber: Random Bits and Pieces

...Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list.

Kent M. Pitman

Adobe PDF Logo

(Cue Mission Impossible theme) Your task is to combine a number of Adobe PDF files into one and to number all pages consecutively. As additional obstacle, not all pages have the same page size and some pages already contain old page numbers, in various places.
Your time starts running... now!

I glued together multiple PDFs with PDFTK, but did not find a suitable tool to remove page numbers and draw new ones on the resulting file. Until I looked at Marc Battyani's neat cl-pdf library, which seemed to have all the ingredients needed. The initial version was developed completely in the REPL, basically in no time, which means I could report back to my boss in time for him to leave and catch the train. Instant local wizard status awarded, too.

Some small amount of glue code later (pdf-page-numbers.lisp), removing the old centered page numbers at coordinates (308,148) and adding new ones in the same place is now as easy as:

(pdf:with-existing-document (#p"old.pdf")
  (with-existing-pages ()
    (clear-page-numbers 308 148 :font "Times-Bold" :font-size 10.0)
    (page-numbers 308 148 :font "Times-Bold" :font-size 10.0))
  (pdf:write-document #p"new.pdf"))

Of course, this looks like it could be packaged into a nice function as well. However, in real life we have to deal with all kinds of nastiness (see above) which would render such a function not very useful. I used the following snippet eventually:

(pdf:with-existing-document (#p"/tmp/old.pdf")
  (with-existing-pages (:range (iota 94 :start 6)
                        :numbering (enumerate :from 1))
    (let ((x (if (evenp *page-number*) 135 480))
          (y (if (<= 50 *page-number* 64) 104 149)))
      (clear-page-numbers 308 y :font "Times-Roman" :font-size 10.0)
      (page-numbers x y :font "Times-Bold" :font-size 10.0 :justify t)))
  (pdf:write-document #p"/tmp/new.pdf"))