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

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/oeb/transforms/flatcss.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:
6
6
__license__   = 'GPL v3'
7
7
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
8
8
 
9
 
import sys
10
 
import os
11
9
import re
12
10
import operator
13
11
import math
14
 
from itertools import chain
15
12
from collections import defaultdict
16
13
from lxml import etree
17
14
from calibre.ebooks.oeb.base import XHTML, XHTML_NS
18
15
from calibre.ebooks.oeb.base import CSS_MIME, OEB_STYLES
19
16
from calibre.ebooks.oeb.base import namespace, barename
20
 
from calibre.ebooks.oeb.base import OEBBook
21
17
from calibre.ebooks.oeb.stylizer import Stylizer
22
18
 
23
19
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
28
24
        value = default
29
25
    return float(value)
30
26
 
 
27
def dynamic_rescale_factor(node):
 
28
    classes = node.get('class', '').split(' ')
 
29
    classes = [x.replace('calibre_rescale_', '') for x in classes if
 
30
            x.startswith('calibre_rescale_')]
 
31
    if not classes: return None
 
32
    factor = 1.0
 
33
    for x in classes:
 
34
        try:
 
35
            factor *= float(x)/100.
 
36
        except ValueError:
 
37
            continue
 
38
    return factor
 
39
 
31
40
 
32
41
class KeyMapper(object):
33
42
    def __init__(self, sbase, dbase, dkey):
48
57
        logb = abs(base - endp)
49
58
        result = sign * math.log(diff, logb)
50
59
        return result
51
 
        
 
60
 
52
61
    def __getitem__(self, ssize):
53
62
        ssize = asfloat(ssize, 0)
54
63
        if ssize in self.cache:
79
88
 
80
89
    def __getitem__(self, ssize):
81
90
        return ssize
82
 
    
 
91
 
83
92
def FontMapper(sbase=None, dbase=None, dkey=None):
84
93
    if sbase and dbase and dkey:
85
94
        return KeyMapper(sbase, dbase, dkey)
98
107
        self.unfloat = unfloat
99
108
        self.untable = untable
100
109
 
101
 
    def transform(self, oeb, context):
 
110
    @classmethod
 
111
    def config(cls, cfg):
 
112
        return cfg
 
113
 
 
114
    @classmethod
 
115
    def generate(cls, opts):
 
116
        return cls()
 
117
 
 
118
    def __call__(self, oeb, context):
102
119
        oeb.logger.info('Flattening CSS and remapping font sizes...')
103
120
        self.oeb = oeb
104
121
        self.context = context
110
127
    def stylize_spine(self):
111
128
        self.stylizers = {}
112
129
        profile = self.context.source
 
130
        css = ''
113
131
        for item in self.oeb.spine:
114
132
            html = item.data
115
 
            stylizer = Stylizer(html, item.href, self.oeb, profile)
 
133
            body = html.find(XHTML('body'))
 
134
            bs = body.get('style', '').split(';')
 
135
            bs.append('margin-top: 0pt')
 
136
            bs.append('margin-bottom: 0pt')
 
137
            bs.append('margin-left : %fpt'%\
 
138
                    float(self.context.margin_left))
 
139
            bs.append('margin-right : %fpt'%\
 
140
                    float(self.context.margin_right))
 
141
            bs.append('text-align: '+ \
 
142
                    ('left' if self.context.dont_justify else 'justify'))
 
143
            body.set('style', '; '.join(bs))
 
144
            stylizer = Stylizer(html, item.href, self.oeb, profile,
 
145
                    user_css=self.context.extra_css,
 
146
                    extra_css=css)
116
147
            self.stylizers[item] = stylizer
117
148
 
 
149
 
118
150
    def baseline_node(self, node, stylizer, sizes, csize):
119
151
        csize = stylizer.style(node)['font-size']
120
152
        if node.text:
123
155
            self.baseline_node(child, stylizer, sizes, csize)
124
156
            if child.tail:
125
157
                sizes[csize] += len(COLLAPSE.sub(' ', child.tail))
126
 
    
 
158
 
127
159
    def baseline_spine(self):
128
160
        sizes = defaultdict(float)
129
161
        for item in self.oeb.spine:
153
185
                else:
154
186
                    value = round(value / slineh) * dlineh
155
187
                    cssdict[property] = "%0.5fem" % (value / fsize)
156
 
    
 
188
 
157
189
    def flatten_node(self, node, stylizer, names, styles, psize, left=0):
158
190
        if not isinstance(node.tag, basestring) \
159
191
           or namespace(node.tag) != XHTML_NS:
167
199
        if node.tag == XHTML('font'):
168
200
            node.tag = XHTML('span')
169
201
            if 'size' in node.attrib:
 
202
                def force_int(raw):
 
203
                    return int(re.search(r'([0-9+-]+)', raw).group(1))
170
204
                size = node.attrib['size'].strip()
171
205
                if size:
172
206
                    fnums = self.context.source.fnums
173
207
                    if size[0] in ('+', '-'):
174
208
                        # Oh, the warcrimes
175
 
                        cssdict['font-size'] = fnums[3+int(size)]
 
209
                        esize = 3 + force_int(size)
 
210
                        if esize < 1:
 
211
                            esize = 1
 
212
                        if esize > 7:
 
213
                            esize = 7
 
214
                        cssdict['font-size'] = fnums[esize]
176
215
                    else:
177
 
                        cssdict['font-size'] = fnums[int(size)]
 
216
                        cssdict['font-size'] = fnums[force_int(size)]
178
217
                del node.attrib['size']
179
218
        if 'color' in node.attrib:
180
219
            cssdict['color'] = node.attrib['color']
182
221
        if 'bgcolor' in node.attrib:
183
222
            cssdict['background-color'] = node.attrib['bgcolor']
184
223
            del node.attrib['bgcolor']
185
 
        if 'font-size' in cssdict or tag == 'body':
186
 
            fsize = self.fmap[style['font-size']]
187
 
            cssdict['font-size'] = "%0.5fem" % (fsize / psize)
188
 
            psize = fsize
 
224
        if not self.context.disable_font_rescaling:
 
225
            _sbase = self.sbase if self.sbase is not None else \
 
226
                self.context.source.fbase
 
227
            dyn_rescale = dynamic_rescale_factor(node)
 
228
            if dyn_rescale is not None:
 
229
                fsize = self.fmap[_sbase]
 
230
                fsize *= dyn_rescale
 
231
                cssdict['font-size'] = '%0.5fem'%(fsize/psize)
 
232
                psize = fsize
 
233
            elif 'font-size' in cssdict or tag == 'body':
 
234
                fsize = self.fmap[style['font-size']]
 
235
                cssdict['font-size'] = "%0.5fem" % (fsize / psize)
 
236
                psize = fsize
189
237
        if cssdict:
190
238
            if self.lineh and self.fbase and tag != 'body':
191
239
                self.clean_edges(cssdict, style, psize)
214
262
        if self.lineh and 'line-height' not in cssdict:
215
263
            lineh = self.lineh / psize
216
264
            cssdict['line-height'] = "%0.5fem" % lineh
 
265
        if (self.context.remove_paragraph_spacing or
 
266
                self.context.insert_blank_line) and tag in ('p', 'div'):
 
267
            for prop in ('margin', 'padding', 'border'):
 
268
                for edge in ('top', 'bottom'):
 
269
                    cssdict['%s-%s'%(prop, edge)] = '0pt'
 
270
            if self.context.insert_blank_line:
 
271
                cssdict['margin-top'] = cssdict['margin-bottom'] = '0.5em'
 
272
            if self.context.remove_paragraph_spacing:
 
273
                cssdict['text-indent'] = '1.5em'
217
274
        if cssdict:
218
275
            items = cssdict.items()
219
276
            items.sort()
220
277
            css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items)
221
 
            classes = node.get('class', None) or 'calibre'
 
278
            classes = node.get('class', '').strip() or 'calibre'
222
279
            klass = STRIPNUM.sub('', classes.split()[0].replace('_', ''))
223
280
            if css in styles:
224
281
                match = styles[css]
248
305
        href = item.relhref(href)
249
306
        etree.SubElement(head, XHTML('link'),
250
307
            rel='stylesheet', type=CSS_MIME, href=href)
251
 
        if stylizer.page_rule:
252
 
            items = stylizer.page_rule.items()
253
 
            items.sort()
254
 
            css = '; '.join("%s: %s" % (key, val) for key, val in items)
255
 
            style = etree.SubElement(head, XHTML('style'), type=CSS_MIME)
256
 
            style.text = "@page { %s; }" % css
 
308
        stylizer.page_rule['margin-top'] = '%fpt'%\
 
309
                float(self.context.margin_top)
 
310
        stylizer.page_rule['margin-bottom'] = '%fpt'%\
 
311
                float(self.context.margin_bottom)
 
312
 
 
313
        items = stylizer.page_rule.items()
 
314
        items.sort()
 
315
        css = '; '.join("%s: %s" % (key, val) for key, val in items)
 
316
        style = etree.SubElement(head, XHTML('style'), type=CSS_MIME)
 
317
        style.text = "@page { %s; }" % css
257
318
 
258
319
    def replace_css(self, css):
259
320
        manifest = self.oeb.manifest
263
324
                manifest.remove(item)
264
325
        item = manifest.add(id, href, CSS_MIME, data=css)
265
326
        return href
266
 
            
 
327
 
267
328
    def flatten_spine(self):
268
329
        names = defaultdict(int)
269
330
        styles = {}
280
341
        for item in self.oeb.spine:
281
342
            stylizer = self.stylizers[item]
282
343
            self.flatten_head(item, stylizer, href)
 
344