Some experiments with the language: html, epub, covers.
[librarian.git] / librarian / book2anything.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
5 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
6 #
7 from collections import namedtuple
8 import os.path
9 import optparse
10
11 from librarian import ParseError
12 from librarian.document import Document
13
14
15 class Option(object):
16     """Option for optparse. Use it like `optparse.OptionParser.add_option`."""
17     def __init__(self, *names, **options):
18         self.names = names
19         self.options = options
20
21     def add(self, parser):
22         parser.add_option(*self.names, **self.options)
23
24     def name(self):
25         return self.options['dest']
26
27     def value(self, options):
28         return getattr(options, self.name())
29
30
31 class Book2Anything(object):
32     """A class for creating book2... scripts.
33     
34     Subclass it for any format you want to convert to.
35     """
36     format_cls = None # A formats.Format subclass
37     document_options = [] # List of Option objects for document options.
38     format_options = [] # List of Option objects for format customization.
39     build_options = [] # List of Option objects for build options.
40
41     @classmethod
42     def run(cls):
43         # Parse commandline arguments
44         usage = """Usage: %%prog [options] SOURCE [SOURCE...]
45         Convert SOURCE files to %s.""" % cls.format_cls.format_name
46
47         parser = optparse.OptionParser(usage=usage)
48
49         parser.add_option('-v', '--verbose', 
50                 action='store_true', dest='verbose', default=False,
51                 help='print status messages to stdout')
52         parser.add_option('-o', '--output-file',
53                 dest='output_file', metavar='FILE',
54                 help='specifies the output file')
55         for option in cls.document_options + cls.format_options + cls.build_options:
56             option.add(parser)
57
58         options, input_filenames = parser.parse_args()
59
60         if len(input_filenames) < 1:
61             parser.print_help()
62             return(1)
63
64         # Prepare additional args for document.
65         document_args = {}
66         for option in cls.document_options:
67             document_args[option.name()] = option.value(options)
68         # Prepare additional args for format.
69         format_args = {}
70         for option in cls.format_options:
71             format_args[option.name()] = option.value(options)
72         # Prepare additional args for build.
73         build_args = {}
74         for option in cls.build_options:
75             build_args[option.name()] = option.value(options)
76
77         # Do some real work
78         try:
79             for main_input in input_filenames:
80                 if options.verbose:
81                     print main_input
82
83             # Do the transformation.
84             doc = Document.from_file(main_input, **document_args)
85             format_ = cls.format_cls(doc, **format_args)
86
87             # Where to write output?
88             if not options.output_file:
89                 output_file = os.path.splitext(main_input)[0] + '.' + format_.format_ext
90             else:
91                 output_file = None
92             
93             output = format_.build(**build_args)
94             output.save_as(output_file)
95
96         except ParseError, e:
97             print '%(file)s:%(name)s:%(message)s' % {
98                 'file': main_input,
99                 'name': e.__class__.__name__,
100                 'message': e
101             }