~ubuntu-branches/ubuntu/karmic/calibre/karmic

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/pdf/manipulate/split.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-30 12:49:41 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090730124941-qjdsmri25zt8zocn
Tags: 0.6.3+dfsg-0ubuntu1
* New upstream release. Please see http://calibre.kovidgoyal.net/new_in_6/
  for the list of new features and changes.
* remove_postinstall.patch: Update for new version.
* build_debug.patch: Does not apply any more, disable for now. Might not be
  necessary any more.
* debian/copyright: Fix reference to versionless GPL.
* debian/rules: Drop obsolete dh_desktop call.
* debian/rules: Add workaround for weird Python 2.6 setuptools behaviour of
  putting compiled .so files into src/calibre/plugins/calibre/plugins
  instead of src/calibre/plugins.
* debian/rules: Drop hal fdi moving, new upstream version does not use hal
  any more. Drop hal dependency, too.
* debian/rules: Install udev rules into /lib/udev/rules.d.
* Add debian/calibre.preinst: Remove unmodified
  /etc/udev/rules.d/95-calibre.rules on upgrade.
* debian/control: Bump Python dependencies to 2.6, since upstream needs
  it now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
from __future__ import with_statement
 
3
 
 
4
__license__   = 'GPL v3'
 
5
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
 
6
__docformat__ = 'restructuredtext en'
 
7
 
 
8
'''
 
9
Split PDF file into multiple PDF documents.
 
10
'''
 
11
 
 
12
import os, sys, re
 
13
from optparse import OptionGroup, Option
 
14
 
 
15
from calibre.ebooks.metadata.meta import metadata_from_formats
 
16
from calibre.ebooks.metadata import authors_to_string
 
17
from calibre.utils.config import OptionParser
 
18
from calibre.utils.logging import Log
 
19
from calibre.constants import preferred_encoding
 
20
from calibre.customize.conversion import OptionRecommendation
 
21
from calibre.ebooks.pdf.verify import is_valid_pdf, is_encrypted
 
22
 
 
23
from pyPdf import PdfFileWriter, PdfFileReader
 
24
 
 
25
USAGE = _('''
 
26
%prog %%name [options] file.pdf page_to_split_on ...
 
27
%prog %%name [options] file.pdf page_range_to_split_on ...
 
28
        
 
29
Ex.
 
30
        
 
31
%prog %%name file.pdf 6
 
32
%prog %%name file.pdf 6-12
 
33
%prog %%name file.pdf 6-12 8 10 9-20
 
34
 
 
35
Split a PDF.
 
36
''')
 
37
 
 
38
OPTIONS = set([
 
39
    OptionRecommendation(name='output', recommended_value='split.pdf',
 
40
        level=OptionRecommendation.HIGH, long_switch='output', short_switch='o',
 
41
        help=_('Path to output file. By default a file is created in the current directory.')),
 
42
])
 
43
 
 
44
def print_help(parser, log):
 
45
    help = parser.format_help().encode(preferred_encoding, 'replace')
 
46
    log(help)
 
47
 
 
48
def option_parser(name):
 
49
    usage = USAGE.replace('%%name', name)
 
50
    return OptionParser(usage=usage)
 
51
 
 
52
def option_recommendation_to_cli_option(add_option, rec):
 
53
    opt = rec.option
 
54
    switches = ['-'+opt.short_switch] if opt.short_switch else []
 
55
    switches.append('--'+opt.long_switch)
 
56
    attrs = dict(dest=opt.name, help=opt.help,
 
57
                     choices=opt.choices, default=rec.recommended_value)
 
58
    add_option(Option(*switches, **attrs))
 
59
 
 
60
def add_options(parser):
 
61
    group = OptionGroup(parser, _('Split Options:'), _('Options to control the transformation of pdf'))
 
62
    parser.add_option_group(group)
 
63
    add_option = group.add_option
 
64
    
 
65
    for rec in OPTIONS:
 
66
        option_recommendation_to_cli_option(add_option, rec)
 
67
 
 
68
def split_pdf(in_path, pages, page_ranges, out_name, metadata=None):
 
69
    pdf = PdfFileReader(open(os.path.abspath(in_path), 'rb'))
 
70
    total_pages = pdf.numPages - 1
 
71
 
 
72
    for index in pages+page_ranges:
 
73
        if index in pages:
 
74
            write_pdf(pdf, out_name, '%s' % (index + 1), index, total_pages, metadata)
 
75
        else:
 
76
            
 
77
            write_pdf(pdf, out_name, '%s-%s' % (index[0] + 1, index[1] + 1), index[0], index[1], metadata)
 
78
        
 
79
def write_pdf(pdf, name, suffix, start, end, metadata=None):
 
80
    if metadata == None:
 
81
        title = _('Unknown')
 
82
        author = _('Unknown')
 
83
    else:
 
84
        title = metadata.title
 
85
        author = authors_to_string(metadata.authors)
 
86
    
 
87
    out_pdf = PdfFileWriter(title=title, author=author)
 
88
    for page_num in range(start, end + 1):
 
89
        out_pdf.addPage(pdf.getPage(page_num))
 
90
    with open('%s%s.pdf' % (name, suffix), 'wb') as out_file:
 
91
        out_pdf.write(out_file)
 
92
    
 
93
def split_args(args):
 
94
    pdf = ''
 
95
    pages = []
 
96
    page_ranges = []
 
97
    bad = []
 
98
 
 
99
    for arg in args:
 
100
        arg = arg.strip()
 
101
        # Find the pdf input
 
102
        if re.search('(?iu)^.*?\.pdf[ ]*$', arg) != None:
 
103
            if pdf == '':
 
104
                pdf = arg
 
105
            else:
 
106
                bad.append(arg)
 
107
        # Find single indexes
 
108
        elif re.search('^[ ]*\d+[ ]*$', arg) != None:
 
109
            pages.append(arg)
 
110
        # Find index ranges
 
111
        elif re.search('^[ ]*\d+[ ]*-[ ]*\d+[ ]*$', arg) != None:
 
112
            mo = re.search('^[ ]*(?P<start>\d+)[ ]*-[ ]*(?P<end>\d+)[ ]*$', arg)
 
113
            start = mo.group('start')
 
114
            end = mo.group('end')
 
115
            
 
116
            # check to see if the range is really a single index
 
117
            if start == end:
 
118
                pages.append(start)
 
119
            else:
 
120
                page_ranges.append([start, end])
 
121
        else:
 
122
            bad.append(arg)
 
123
        
 
124
    bad = sorted(list(set(bad)))
 
125
    
 
126
    return pdf, pages, page_ranges, bad
 
127
 
 
128
# Remove duplicates from pages and page_ranges.
 
129
# Set pages higher than the total number of pages in the pdf to the last page.
 
130
# Return pages and page_ranges as lists of ints.
 
131
def clean_page_list(pdf_path, pages, page_ranges):
 
132
    pdf = PdfFileReader(open(os.path.abspath(pdf_path), 'rb'))
 
133
    
 
134
    total_pages = pdf.numPages
 
135
    sorted_pages = []
 
136
    sorted_ranges = []
 
137
 
 
138
    for index in pages:
 
139
        index = int(index)
 
140
        if index > total_pages:
 
141
            sorted_pages.append(total_pages - 1)
 
142
        else:
 
143
            sorted_pages.append(index - 1)
 
144
    
 
145
    for start, end in page_ranges:
 
146
        start = int(start)
 
147
        end = int(end)
 
148
        
 
149
        if start > total_pages and end > total_pages:
 
150
            sorted_pages.append(total_pages - 1)
 
151
            continue
 
152
            
 
153
        if start > total_pages:
 
154
            start = total_pages
 
155
        if end > total_pages:
 
156
            end = total_pages
 
157
        page_range = sorted([start - 1, end - 1])
 
158
        if page_range not in sorted_ranges:
 
159
            sorted_ranges.append(page_range)
 
160
    
 
161
    # Remove duplicates and sort
 
162
    pages = sorted(list(set(sorted_pages)))
 
163
    page_ranges = sorted(sorted_ranges)
 
164
    
 
165
    return pages, page_ranges
 
166
 
 
167
def main(args=sys.argv, name=''):
 
168
    log = Log()
 
169
    parser = option_parser(name)
 
170
    add_options(parser)
 
171
    
 
172
    opts, args = parser.parse_args(args)
 
173
    
 
174
    pdf, pages, page_ranges, unknown = split_args(args[1:])
 
175
    
 
176
    if pdf == '' and (pages == [] or page_ranges == []):
 
177
        print 'Error: PDF and where to split is required.\n'
 
178
        print_help(parser, log)
 
179
        return 1
 
180
    
 
181
    if unknown != []:
 
182
        for arg in unknown:
 
183
            print 'Error: Unknown argument `%s`' % arg
 
184
        print_help(parser, log)
 
185
        return 1
 
186
    
 
187
    if not is_valid_pdf(pdf):
 
188
        print 'Error: Could not read file `%s`.' % pdf
 
189
        return 1
 
190
        
 
191
    if is_encrypted(pdf):
 
192
        print 'Error: file `%s` is encrypted.' % args[0]
 
193
        return 1
 
194
        
 
195
    pages, page_ranges = clean_page_list(pdf, pages, page_ranges)
 
196
        
 
197
    mi = metadata_from_formats([pdf])
 
198
 
 
199
    split_pdf(pdf, pages, page_ranges, os.path.splitext(opts.output)[0], mi)
 
200
 
 
201
    return 0
 
202
 
 
203
if __name__ == '__main__':
 
204
    sys.exit(main())