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

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/fb2/fb2ml.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
 
 
3
__license__ = 'GPL 3'
 
4
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
 
5
__docformat__ = 'restructuredtext en'
 
6
 
 
7
'''
 
8
Transform OEB content into FB2 markup
 
9
'''
 
10
 
 
11
import os
 
12
from base64 import b64encode
 
13
 
 
14
from lxml import etree
 
15
 
 
16
from calibre.ebooks.oeb.base import XHTML, XHTML_NS, barename, namespace
 
17
from calibre.ebooks.oeb.stylizer import Stylizer
 
18
from calibre.ebooks.oeb.base import OEB_IMAGES
 
19
from calibre.constants import __appname__, __version__
 
20
 
 
21
TAG_MAP = {
 
22
    'b' : 'strong',
 
23
    'i' : 'emphasis',
 
24
    'p' : 'p',
 
25
    'li' : 'p'
 
26
}
 
27
 
 
28
STYLES = [
 
29
    ('font-weight', {'bold'   : 'strong', 'bolder' : 'strong'}),
 
30
    ('font-style', {'italic' : 'emphasis'}),
 
31
]
 
32
 
 
33
class FB2MLizer(object):
 
34
    def __init__(self, log):
 
35
        self.log = log
 
36
        
 
37
    def extract_content(self, oeb_book, opts):
 
38
        self.log.info('Converting XHTML to FB2 markup...')
 
39
        self.oeb_book = oeb_book
 
40
        self.opts = opts
 
41
        return self.fb2mlize_spine()
 
42
        
 
43
    def fb2mlize_spine(self):
 
44
        output = self.fb2_header()
 
45
        if 'titlepage' in self.oeb_book.guide:
 
46
            self.log.debug('Generating cover page...')
 
47
            href = self.oeb_book.guide['titlepage'].href
 
48
            item = self.oeb_book.manifest.hrefs[href]
 
49
            if item.spine_position is None:
 
50
                stylizer = Stylizer(item.data, item.href, self.oeb_book, self.opts.output_profile)
 
51
                output += self.dump_text(item.data.find(XHTML('body')), stylizer)
 
52
        for item in self.oeb_book.spine:
 
53
            self.log.debug('Converting %s to FictionBook2 XML' % item.href)
 
54
            stylizer = Stylizer(item.data, item.href, self.oeb_book, self.opts.output_profile)
 
55
            output += self.dump_text(item.data.find(XHTML('body')), stylizer)
 
56
        output += self.fb2_body_footer()
 
57
        output += self.fb2mlize_images()
 
58
        output += self.fb2_footer()
 
59
        output = self.clean_text(output)
 
60
        return u'<?xml version="1.0" encoding="UTF-8"?>\n%s' % etree.tostring(etree.fromstring(output), encoding=unicode, pretty_print=True)
 
61
 
 
62
    def fb2_header(self):
 
63
        return u'<FictionBook xmlns:xlink="http://www.w3.org/1999/xlink" ' \
 
64
        'xmlns="http://www.gribuser.ru/xml/fictionbook/2.0">\n' \
 
65
        '<description>\n<title-info><book-title>%s</book-title> ' \
 
66
        '</title-info><document-info> ' \
 
67
        '<program-used>%s - %s</program-used></document-info>\n' \
 
68
        '</description>\n<body>\n<section>' % (self.oeb_book.metadata.title[0].value, __appname__, __version__)
 
69
        
 
70
    def fb2_body_footer(self):
 
71
        return u'\n</section>\n</body>'
 
72
        
 
73
    def fb2_footer(self):
 
74
        return u'</FictionBook>'
 
75
 
 
76
    def fb2mlize_images(self):
 
77
        images = u''
 
78
        for item in self.oeb_book.manifest:
 
79
            if item.media_type in OEB_IMAGES:
 
80
                raw_data = b64encode(item.data)
 
81
                # Don't put the encoded image on a single line.
 
82
                data = ''
 
83
                col = 1
 
84
                for char in raw_data:
 
85
                    if col == 72:
 
86
                        data += '\n'
 
87
                        col = 1
 
88
                    col += 1
 
89
                    data += char
 
90
                images += '<binary id="%s" content-type="%s">%s\n</binary>' % (os.path.basename(item.href),  item.media_type, data)
 
91
        return images
 
92
 
 
93
    def clean_text(self, text):
 
94
        text = text.replace('&', '')
 
95
 
 
96
        return text
 
97
 
 
98
    def dump_text(self, elem, stylizer, tag_stack=[]):
 
99
        if not isinstance(elem.tag, basestring) \
 
100
           or namespace(elem.tag) != XHTML_NS:
 
101
            return u''
 
102
            
 
103
        fb2_text = u''
 
104
        style = stylizer.style(elem)
 
105
 
 
106
        if style['display'] in ('none', 'oeb-page-head', 'oeb-page-foot') \
 
107
           or style['visibility'] == 'hidden':
 
108
            return u''
 
109
        
 
110
        tag = barename(elem.tag)
 
111
        tag_count = 0
 
112
 
 
113
        if tag == 'img':
 
114
            fb2_text += '<image xlink:href="#%s" />' % os.path.basename(elem.attrib['src'])
 
115
        
 
116
 
 
117
        fb2_tag = TAG_MAP.get(tag, None)
 
118
        if fb2_tag and fb2_tag not in tag_stack:
 
119
            tag_count += 1
 
120
            fb2_text += '<%s>' % fb2_tag
 
121
            tag_stack.append(fb2_tag)
 
122
 
 
123
 
 
124
        # Processes style information
 
125
        for s in STYLES:
 
126
            style_tag = s[1].get(style[s[0]], None)
 
127
            if style_tag:
 
128
                tag_count += 1
 
129
                fb2_text += '<%s>' % style_tag
 
130
                tag_stack.append(style_tag)
 
131
 
 
132
        if hasattr(elem, 'text') and elem.text != None and elem.text.strip() != '':
 
133
            fb2_text += elem.text
 
134
        
 
135
        for item in elem:
 
136
            fb2_text += self.dump_text(item, stylizer, tag_stack)
 
137
 
 
138
        close_tag_list = []
 
139
        for i in range(0, tag_count):
 
140
            close_tag_list.insert(0, tag_stack.pop())
 
141
        fb2_text += self.close_tags(close_tag_list)
 
142
 
 
143
        if hasattr(elem, 'tail') and elem.tail != None and elem.tail.strip() != '':
 
144
            if 'p' not in tag_stack:
 
145
                fb2_text += '<p>%s</p>' % elem.tail
 
146
            else:
 
147
                fb2_text += elem.tail
 
148
            
 
149
        return fb2_text
 
150
 
 
151
    def close_tags(self, tags):
 
152
        fb2_text = u''
 
153
        for i in range(0, len(tags)):
 
154
            fb2_tag = tags.pop()
 
155
            fb2_text += '</%s>' % fb2_tag
 
156
 
 
157
        return fb2_text