~stub/ubuntu/precise/calibre/devel

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/txt/input.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-04-12 11:29:25 UTC
  • mfrom: (42.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110412112925-c7171kt2bb5rmft4
Tags: 0.7.50+dfsg-2
* debian/control: Build with libpodofo-dev to enable PDF metadata.
  (Closes: #619632)
* debian/control: Add libboost1.42-dev build dependency. Apparently it is
  needed in some setups. (Closes: #619807)
* debian/rules: Call dh_sip to generate a proper sip API dependency, to
  prevent crashes like #616372 for partial upgrades.
* debian/control: Bump python-qt4 dependency to >= 4.8.3-2, which reportedly
  fixes crashes on startup. (Closes: #619701, #620125)

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 
7
7
import os
8
8
 
 
9
from calibre import _ent_pat, walk, xml_entity_to_unicode
9
10
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
10
11
from calibre.ebooks.conversion.preprocess import DocAnalysis, Dehyphenator
11
12
from calibre.ebooks.chardet import detect
12
13
from calibre.ebooks.txt.processor import convert_basic, convert_markdown, \
13
14
    separate_paragraphs_single_line, separate_paragraphs_print_formatted, \
14
15
    preserve_spaces, detect_paragraph_type, detect_formatting_type, \
15
 
    normalize_line_endings, convert_textile
16
 
from calibre import _ent_pat, xml_entity_to_unicode
 
16
    normalize_line_endings, convert_textile, remove_indents, block_to_single_line, \
 
17
    separate_hard_scene_breaks
 
18
from calibre.utils.zipfile import ZipFile
17
19
 
18
20
class TXTInput(InputFormatPlugin):
19
21
 
20
22
    name        = 'TXT Input'
21
23
    author      = 'John Schember'
22
24
    description = 'Convert TXT files to HTML'
23
 
    file_types  = set(['txt'])
 
25
    file_types  = set(['txt', 'txtz', 'text'])
24
26
 
25
27
    options = set([
26
28
        OptionRecommendation(name='paragraph_type', recommended_value='auto',
27
 
            choices=['auto', 'block', 'single', 'print', 'unformatted'],
 
29
            choices=['auto', 'block', 'single', 'print', 'unformatted', 'off'],
28
30
            help=_('Paragraph structure.\n'
29
 
                   'choices are [\'auto\', \'block\', \'single\', \'print\', \'unformatted\']\n'
 
31
                   'choices are [\'auto\', \'block\', \'single\', \'print\', \'unformatted\', \'off\']\n'
30
32
                   '* auto: Try to auto detect paragraph type.\n'
31
33
                   '* block: Treat a blank line as a paragraph break.\n'
32
34
                   '* single: Assume every line is a paragraph.\n'
33
35
                   '* print:  Assume every line starting with 2+ spaces or a tab '
34
 
                   'starts a paragraph.'
35
 
                   '* unformatted: Most lines have hard line breaks, few/no blank lines or indents.')),
 
36
                   'starts a paragraph.\n'
 
37
                   '* unformatted: Most lines have hard line breaks, few/no blank lines or indents. '
 
38
                   'Tries to determine structure and reformat the differentiate elements.\n'
 
39
                   '* off: Don\'t modify the paragraph structure. This is useful when combined with '
 
40
                   'Markdown or Textile formatting to ensure no formatting is lost.')),
36
41
        OptionRecommendation(name='formatting_type', recommended_value='auto',
37
 
            choices=['auto', 'none', 'heuristic', 'textile', 'markdown'],
 
42
            choices=['auto', 'plain', 'heuristic', 'textile', 'markdown'],
38
43
            help=_('Formatting used within the document.'
39
44
                   '* auto: Automatically decide which formatting processor to use.\n'
40
 
                   '* none: Do not process the document formatting. Everything is a '
 
45
                   '* plain: Do not process the document formatting. Everything is a '
41
46
                   'paragraph and no styling is applied.\n'
42
47
                   '* heuristic: Process using heuristics to determine formatting such '
43
48
                   'as chapter headings and italic text.\n'
47
52
        OptionRecommendation(name='preserve_spaces', recommended_value=False,
48
53
            help=_('Normally extra spaces are condensed into a single space. '
49
54
                'With this option all spaces will be displayed.')),
 
55
        OptionRecommendation(name='txt_in_remove_indents', recommended_value=False,
 
56
            help=_('Normally extra space at the beginning of lines is retained. '
 
57
                   'With this option they will be removed.')),
50
58
        OptionRecommendation(name="markdown_disable_toc", recommended_value=False,
51
59
            help=_('Do not insert a Table of Contents into the output text.')),
52
60
    ])
54
62
    def convert(self, stream, options, file_ext, log,
55
63
                accelerators):
56
64
        self.log = log
 
65
        txt = ''
57
66
        log.debug('Reading text from file...')
58
 
 
59
 
        txt = stream.read()
 
67
        length = 0
 
68
 
 
69
        # Extract content from zip archive.
 
70
        if file_ext == 'txtz':
 
71
            zf = ZipFile(stream)
 
72
            zf.extractall('.')
 
73
 
 
74
            for x in walk('.'):
 
75
                if os.path.splitext(x)[1].lower() in ('.txt', '.text'):
 
76
                    with open(x, 'rb') as tf:
 
77
                        txt += tf.read() + '\n\n'
 
78
        else:
 
79
            txt = stream.read()
 
80
 
60
81
        # Get the encoding of the document.
61
82
        if options.input_encoding:
62
83
            ienc = options.input_encoding
70
91
            log.debug('No input encoding specified and could not auto detect using %s' % ienc)
71
92
        txt = txt.decode(ienc, 'replace')
72
93
 
 
94
        # Replace entities
73
95
        txt = _ent_pat.sub(xml_entity_to_unicode, txt)
74
96
 
75
97
        # Normalize line endings
76
98
        txt = normalize_line_endings(txt)
77
99
 
78
 
        if options.formatting_type == 'auto':
79
 
            options.formatting_type = detect_formatting_type(txt)
80
 
 
81
 
        if options.formatting_type == 'heuristic':
82
 
            setattr(options, 'enable_heuristics', True)
83
 
            setattr(options, 'markup_chapter_headings', True)
84
 
            setattr(options, 'italicize_common_cases', True)
85
 
            setattr(options, 'fix_indents', True)
86
 
            setattr(options, 'delete_blank_paragraphs', True)
87
 
            setattr(options, 'format_scene_breaks', True)
88
 
            setattr(options, 'dehyphenate', True)
89
 
 
90
100
        # Determine the paragraph type of the document.
91
101
        if options.paragraph_type == 'auto':
92
102
            options.paragraph_type = detect_paragraph_type(txt)
96
106
            else:
97
107
                log.debug('Auto detected paragraph type as %s' % options.paragraph_type)
98
108
 
 
109
        # Detect formatting
 
110
        if options.formatting_type == 'auto':
 
111
            options.formatting_type = detect_formatting_type(txt)
 
112
            log.debug('Auto detected formatting as %s' % options.formatting_type)
 
113
 
 
114
        if options.formatting_type == 'heuristic':
 
115
            setattr(options, 'enable_heuristics', True)
 
116
            setattr(options, 'unwrap_lines', False)
 
117
            setattr(options, 'smarten_punctuation', True)
 
118
 
 
119
        # Reformat paragraphs to block formatting based on the detected type.
 
120
        # We don't check for block because the processor assumes block.
 
121
        # single and print at transformed to block for processing.
 
122
        if options.paragraph_type == 'single':
 
123
            txt = separate_paragraphs_single_line(txt)
 
124
        elif options.paragraph_type == 'print':
 
125
            txt = separate_hard_scene_breaks(txt)
 
126
            txt = separate_paragraphs_print_formatted(txt)
 
127
            txt = block_to_single_line(txt)
 
128
        elif options.paragraph_type == 'unformatted':
 
129
            from calibre.ebooks.conversion.utils import HeuristicProcessor
 
130
            # unwrap lines based on punctuation
 
131
            docanalysis = DocAnalysis('txt', txt)
 
132
            length = docanalysis.line_length(.5)
 
133
            preprocessor = HeuristicProcessor(options, log=getattr(self, 'log', None))
 
134
            txt = preprocessor.punctuation_unwrap(length, txt, 'txt')
 
135
            txt = separate_paragraphs_single_line(txt)
 
136
        elif options.paragraph_type == 'block':
 
137
            txt = separate_hard_scene_breaks(txt)
 
138
            txt = block_to_single_line(txt)
 
139
 
 
140
        if getattr(options, 'enable_heuristics', False) and getattr(options, 'dehyphenate', False):
 
141
            docanalysis = DocAnalysis('txt', txt)
 
142
            if not length:
 
143
                length = docanalysis.line_length(.5)
 
144
            dehyphenator = Dehyphenator(options.verbose, log=self.log)
 
145
            txt = dehyphenator(txt,'txt', length)
 
146
 
 
147
        # User requested transformation on the text.
 
148
        if options.txt_in_remove_indents:
 
149
            txt = remove_indents(txt)
 
150
 
99
151
        # Preserve spaces will replace multiple spaces to a space
100
152
        # followed by the   entity.
101
153
        if options.preserve_spaces:
102
154
            txt = preserve_spaces(txt)
103
155
 
104
 
        # Get length for hyphen removal and punctuation unwrap
105
 
        docanalysis = DocAnalysis('txt', txt)
106
 
        length = docanalysis.line_length(.5)
107
 
 
 
156
        # Process the text using the appropriate text processor.
 
157
        html = ''
108
158
        if options.formatting_type == 'markdown':
109
 
            log.debug('Running text though markdown conversion...')
 
159
            log.debug('Running text through markdown conversion...')
110
160
            try:
111
161
                html = convert_markdown(txt, disable_toc=options.markdown_disable_toc)
112
162
            except RuntimeError:
113
163
                raise ValueError('This txt file has malformed markup, it cannot be'
114
164
                    ' converted by calibre. See http://daringfireball.net/projects/markdown/syntax')
115
165
        elif options.formatting_type == 'textile':
116
 
            log.debug('Running text though textile conversion...')
 
166
            log.debug('Running text through textile conversion...')
117
167
            html = convert_textile(txt)
118
 
 
119
168
        else:
120
 
            # Dehyphenate
121
 
            dehyphenator = Dehyphenator(options.verbose, log=self.log)
122
 
            txt = dehyphenator(txt,'txt', length)
123
 
 
124
 
            # We don't check for block because the processor assumes block.
125
 
            # single and print at transformed to block for processing.
126
 
 
127
 
            if options.paragraph_type == 'single' or options.paragraph_type == 'unformatted':
128
 
                txt = separate_paragraphs_single_line(txt)
129
 
            elif options.paragraph_type == 'print':
130
 
                txt = separate_paragraphs_print_formatted(txt)
131
 
 
132
 
            if options.paragraph_type == 'unformatted':
133
 
                from calibre.ebooks.conversion.utils import HeuristicProcessor
134
 
                # get length
135
 
 
136
 
                # unwrap lines based on punctuation
137
 
                preprocessor = HeuristicProcessor(options, log=getattr(self, 'log', None))
138
 
                txt = preprocessor.punctuation_unwrap(length, txt, 'txt')
139
 
 
 
169
            log.debug('Running text through basic conversion...')
140
170
            flow_size = getattr(options, 'flow_size', 0)
141
171
            html = convert_basic(txt, epub_split_size_kb=flow_size)
142
172
 
 
173
        # Run the HTMLized text through the html processing plugin.
143
174
        from calibre.customize.ui import plugin_for_input_format
144
175
        html_input = plugin_for_input_format('html')
145
176
        for opt in html_input.options:
146
177
            setattr(options, opt.option.name, opt.recommended_value)
147
178
        options.input_encoding = 'utf-8'
148
179
        base = os.getcwdu()
149
 
        if hasattr(stream, 'name'):
 
180
        if file_ext != 'txtz' and hasattr(stream, 'name'):
150
181
            base = os.path.dirname(stream.name)
151
182
        fname = os.path.join(base, 'index.html')
152
183
        c = 0
158
189
            htmlfile.write(html.encode('utf-8'))
159
190
        odi = options.debug_pipeline
160
191
        options.debug_pipeline = None
 
192
        # Generate oeb from html conversion.
161
193
        oeb = html_input.convert(open(htmlfile.name, 'rb'), options, 'html', log,
162
194
                {})
163
195
        options.debug_pipeline = odi
164
196
        os.remove(htmlfile.name)
 
197
 
 
198
        # Set metadata from file.
 
199
        from calibre.customize.ui import get_file_type_metadata
 
200
        from calibre.ebooks.oeb.transforms.metadata import meta_info_to_oeb_metadata
 
201
        mi = get_file_type_metadata(stream, file_ext)
 
202
        meta_info_to_oeb_metadata(mi, oeb.metadata, log)
 
203
 
165
204
        return oeb