PythonでPDFを分割する (すべての自炊派バンドマンに捧げる)

やりたいこと

バンドスコアをスキャンする → 楽曲ごとに分割したPDFにしたい

やりかた

pyPdfっていうモジュールを使うとできる。

sudo easy_install pypdf

サンプルを参考に練習がてら書いた。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pyPdf import PdfFileWriter, PdfFileReader

def main():
    """sample.pdf を 3ページごとに分割する"""
    src = 'sample.pdf'
    div = 3
    
    src = PdfFileReader(file(src, 'rb'))
    items = [ range(i, min(i + div, src.numPages))
              for i in xrange(0, src.numPages, div) ]
    
    for i, lst  in enumerate(items):
        print i, lst
        dst = PdfFileWriter()
        for j in lst:
            dst.addPage(src.getPage(j))
        out = file('%03d.pdf' % i, 'wb')
        dst.write(out)
        out.close()

if __name__ == '__main__':
    main()

なにこれ超簡単。

ちゃんと書いた

分割用の目次(YAML)を定義して、それをつかって分割させる。こんな感じのやつ。

indexes:
  # 書式
  # (a[,b]) filename
  
  # (a) filename => 'ページa のみ抽出'
  - (  1     ) foo    # p. 1 only

  # (a,b) filename => 'ページa から ページb までを抽出'
  - (  5,   8) bar    # from p. 5 to p. 8

  # b を省略 => '次の項目まで抽出'
  - (  9,    ) baz    # from p. 9 to p.11

  # 最後の要素で b を省略 => '最後のページまで抽出'
  - ( 12,    ) hoge   # from p.12 to last
  
# 分割対象の最後のページ(省略した場合はPDFの最後のページ)
lastPage: 9999 

他にファイル名の前後につける文字や、元のPDFが何ページから始まるかを指定できるようにしてあげる。


あとはコードを書く。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, re, optparse

import yaml
import pyPdf

def getprofile(profile):
    profile = yaml.load(open(profile))
    prefix = profile.get('prefix', '')
    suffix = profile.get('suffix', '')
    fnum = profile.get('fnum',  0)
    lastPage = profile.get('lastPage')
    indexes = profile.get('indexes', [])
    
    fname = ' '.join([prefix, '%(name)s', suffix]).strip()

    return indexes, lastPage, (fname, fnum)

def makeindexes(indexes):
    x = re.compile(r'\(\s*([0-9]+)\s*(?:(,)\s*([0-9]+)?\s*)?\)\s*(.*)$')
    
    tmp = []
    for i in xrange(len(indexes)):
        m = x.match(indexes[i])
        if not m:
            print 'Syntax Error: line=%d\n  - %s' % (i+1, indexes[i])
            exit(1)
        start = stop = int(m.group(1))
        if m.group(2):
            stop = int(m.group(3)) if m.group(3) else None
        name  = m.group(4).strip()
        tmp.append((start, stop, name))

    return tmp

def main():
    parser = optparse.OptionParser(usage='%prog [Options] src')
    parser.add_option("-o", "--offset", type="int", default=0)
    parser.add_option("-p", "--profile")
    (opts, args) = parser.parse_args()
    
    if not args:
        parser.print_help()
        exit(0)
    
    src = args[0]
    profile = opts.profile or src
    offset  = opts.offset
    
    if not src.lower().endswith('pdf'):
        src += '.pdf'
    
    if not profile.lower().endswith('yaml'):
        profile += '.yaml'
    
    indexes, lastPage, (fname, fnum) = getprofile(profile)
    indexes = makeindexes(indexes)
    
    print 'Divide PDF: %s (using %s)' % (src, profile)
    
    src = pyPdf.PdfFileReader(file(src, 'rb'))
    if lastPage is None:
        lastPage = src.numPages
    else:
        lastPage = min(lastPage, src.numPages)
    
    for i, (start, stop, name) in enumerate(indexes):
        dst = pyPdf.PdfFileWriter()
        if stop is None:
            if i < len(indexes) -1:
                stop = indexes[i+1][0] - 1
            else:
                stop = lastPage
        
        filename = fname % dict(fnum=(fnum+i), name=name)
        print '  page(%4d-%4d) => %s.pdf' % (start, stop, filename)
        for j in xrange(start, stop+1):
            if j + offset < lastPage:
                dst.addPage(src.getPage(j + offset - 1))
        
        out = file('%s.pdf' % filename, 'wb')
        dst.write(out)
        out.close()

if __name__ == '__main__':
    main()
prefix: 'The Beatles %(fnum)02d'
fnum: 1

indexes:
  - (  2,) LOVE ME DO
  - (  8,) PLEASE PLEASE ME
  - ( 14,) FROM ME TO YOU
  - ( 19,) SHE LOVES YOU
  - ( 26,) I WANT TO HOLD YOUR HAND
  - ( 33,) ALL MY LOVING
  - ( 39,) CAN'T BUY ME LOVE
  - ( 48,) A HARD DAY'S NIGHT
  - ( 56,) AND I LOVE HER
  - ( 62,) EIGHT DAYS A WEEK
  - ( 67,) I FEEL FINE
  - ( 75,) TICKET TO RIDE
  - ( 82,) YESTERDAY
  - ( 84,) HELP
  - ( 91,) YOU'VE GOT TO HIDE YOUR LOVE AWAY
  - ( 95,) WE CAN WORK IT OUT
  - (101,) DAY TRIPPER
  - (111,) DRIVE MY CAR
  - (118,) NORWEGIAN WOOD
  - (122,) NOWHERE MAN
  - (128,) MICHELLE
  - (136,) IN MY LIFE
  - (143,) GIRL
  - (149,) PAPERBACK WRITER
  - (155,) ELEANOR RIGBY
  - (160,) YELLOW SUBMARINE

https://gist.github.com/1177605

雑感

Pdfっていう書き方ダサいですよね?