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

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/rb/writer.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
import os
 
8
import struct
 
9
import zlib
 
10
 
 
11
try:
 
12
    from PIL import Image
 
13
    Image
 
14
except ImportError:
 
15
    import Image
 
16
 
 
17
import cStringIO
 
18
 
 
19
from calibre.ebooks.rb.rbml import RBMLizer
 
20
from calibre.ebooks.rb import HEADER
 
21
from calibre.ebooks.rb import unique_name
 
22
from calibre.ebooks.oeb.base import OEB_IMAGES
 
23
from calibre.constants import __appname__, __version__
 
24
 
 
25
TEXT_RECORD_SIZE = 4096
 
26
 
 
27
class TocItem(object):
 
28
 
 
29
    def __init__(self, name, size, flags):
 
30
        self.name = name
 
31
        self.size = size
 
32
        self.flags = flags
 
33
 
 
34
 
 
35
class RBWriter(object):
 
36
 
 
37
    def __init__(self, opts, log):
 
38
        self.opts = opts
 
39
        self.log = log
 
40
        self.name_map = {}
 
41
 
 
42
    def write_content(self, oeb_book, out_stream, metadata=None):
 
43
        info = [('info.info', self._info_section(metadata))]
 
44
        images = self._images(oeb_book.manifest)
 
45
        text_size, chuncks = self._text(oeb_book)
 
46
        chunck_sizes = [len(x) for x in chuncks]
 
47
        text = [('index.html', chuncks)]
 
48
        hidx = [('index.hidx', ' ')]
 
49
 
 
50
        toc_items = []
 
51
        page_count = 0
 
52
        for name, data in info+text+hidx+images:
 
53
            page_count += 1
 
54
            size = len(data)
 
55
            if (name, data) in text:
 
56
                flags = 8
 
57
                size = 0
 
58
                for c in chunck_sizes:
 
59
                    size += c
 
60
                size += 8 + (len(chunck_sizes) * 4)
 
61
            elif (name, data) in info:
 
62
                flags = 2
 
63
            else:
 
64
                flags = 0
 
65
            toc_items.append(TocItem(name.ljust(32, '\x00')[:32], size, flags))
 
66
 
 
67
        self.log.debug('Writing file header...')
 
68
        out_stream.write(HEADER)
 
69
        out_stream.write(struct.pack('<I', 0))
 
70
        out_stream.write(struct.pack('<IH', 0, 0))
 
71
        out_stream.write(struct.pack('<I', 0x128))
 
72
        out_stream.write(struct.pack('<I', 0))
 
73
        for i in range(0x20, 0x128, 4):
 
74
            out_stream.write(struct.pack('<I', 0))
 
75
        out_stream.write(struct.pack('<I', page_count))
 
76
        offset = out_stream.tell() + (len(toc_items) * 44)
 
77
        for item in toc_items:
 
78
            out_stream.write(item.name)
 
79
            out_stream.write(struct.pack('<I', item.size))
 
80
            out_stream.write(struct.pack('<I', offset))
 
81
            out_stream.write(struct.pack('<I', item.flags))
 
82
            offset += item.size
 
83
 
 
84
        out_stream.write(info[0][1])
 
85
 
 
86
        self.log.debug('Writing compressed RB HTHML...')
 
87
        # Compressed text with proper heading
 
88
        out_stream.write(struct.pack('<I', len(text[0][1])))
 
89
        out_stream.write(struct.pack('<I', text_size))
 
90
        for size in chunck_sizes:
 
91
            out_stream.write(struct.pack('<I', size))
 
92
        for chunck in text[0][1]:
 
93
            out_stream.write(chunck)
 
94
 
 
95
        self.log.debug('Writing images...')
 
96
        for item in hidx+images:
 
97
            out_stream.write(item[1])
 
98
 
 
99
        total_size = out_stream.tell()
 
100
        out_stream.seek(0x1c)
 
101
        out_stream.write(struct.pack('<I', total_size))
 
102
 
 
103
    def _text(self, oeb_book):
 
104
        rbmlizer = RBMLizer(self.log, name_map=self.name_map)
 
105
        text = rbmlizer.extract_content(oeb_book, self.opts).encode('cp1252', 'xmlcharrefreplace')
 
106
        size = len(text)
 
107
 
 
108
        pages = []
 
109
        for i in range(0, (len(text) / TEXT_RECORD_SIZE) + 1):
 
110
            pages.append(zlib.compress(text[i * TEXT_RECORD_SIZE : (i * TEXT_RECORD_SIZE) + TEXT_RECORD_SIZE], 9))
 
111
 
 
112
        return (size, pages)
 
113
 
 
114
    def _images(self, manifest):
 
115
        images = []
 
116
        used_names = []
 
117
 
 
118
        for item in manifest:
 
119
            if item.media_type in OEB_IMAGES:
 
120
                data = ''
 
121
 
 
122
                im = Image.open(cStringIO.StringIO(item.data)).convert('L')
 
123
                data = cStringIO.StringIO()
 
124
                im.save(data, 'PNG')
 
125
                data = data.getvalue()
 
126
 
 
127
                name = '%s.png' % os.path.splitext(os.path.basename(item.href))[0]
 
128
                name = unique_name(name, used_names)
 
129
                used_names.append(name)
 
130
                self.name_map[os.path.basename(item.href)] = name
 
131
 
 
132
                images.append((name, data))
 
133
 
 
134
        return images
 
135
 
 
136
    def _info_section(self, metadata):
 
137
        text = 'TYPE=2\n'
 
138
        if metadata:
 
139
            if len(metadata.title) >= 1:
 
140
                text += 'TITLE=%s\n' % metadata.title[0].value
 
141
            if len(metadata.creator) >= 1:
 
142
                from calibre.ebooks.metadata import authors_to_string
 
143
                text += 'AUTHOR=%s\n' % authors_to_string([x.value for x in metadata.creator])
 
144
        text += 'GENERATOR=%s - %s\n' % (__appname__, __version__)
 
145
        text += 'PARSE=1\n'
 
146
        text += 'OUTPUT=1\n'
 
147
        text += 'BODY=index.html\n'
 
148
 
 
149
        return text
 
150