Mobipocket support:

- Add 'vertical-align: text-top' as a <sup/>-generating case
  - Add titles to title-less known <guide/> <reference/>s and cleanly
    ignoren any others
  - Add transform to generate results of CSS 'text-transform' and
    'font-variant'
This commit is contained in:
Marshall T. Vandegrift 2009-01-11 15:41:34 -05:00
parent ed6f3b6702
commit 2397ee84bf
4 changed files with 139 additions and 3 deletions

View File

@ -303,7 +303,7 @@ class MobiMLizer(object):
else: else:
istate.family = 'serif' istate.family = 'serif'
valign = style['vertical-align'] valign = style['vertical-align']
if valign in ('super', 'sup') or asfloat(valign) > 0: if valign in ('super', 'text-top') or asfloat(valign) > 0:
istate.valign = 'super' istate.valign = 'super'
elif valign == 'sub' or asfloat(valign) < 0: elif valign == 'sub' or asfloat(valign) < 0:
istate.valign = 'sub' istate.valign = 'sub'

View File

@ -28,6 +28,7 @@ from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener
from calibre.ebooks.oeb.transforms.rasterize import SVGRasterizer from calibre.ebooks.oeb.transforms.rasterize import SVGRasterizer
from calibre.ebooks.oeb.transforms.trimmanifest import ManifestTrimmer from calibre.ebooks.oeb.transforms.trimmanifest import ManifestTrimmer
from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder
from calibre.ebooks.oeb.transforms.manglecase import CaseMangler
from calibre.ebooks.mobi.palmdoc import compress_doc from calibre.ebooks.mobi.palmdoc import compress_doc
from calibre.ebooks.mobi.langcodes import iana2mobi from calibre.ebooks.mobi.langcodes import iana2mobi
from calibre.ebooks.mobi.mobiml import MBP_NS, MBP, MobiMLizer from calibre.ebooks.mobi.mobiml import MBP_NS, MBP, MobiMLizer
@ -114,7 +115,8 @@ class Serializer(object):
buffer.write('<guide>') buffer.write('<guide>')
for ref in self.oeb.guide.values(): for ref in self.oeb.guide.values():
path, frag = urldefrag(ref.href) path, frag = urldefrag(ref.href)
if hrefs[path].media_type not in OEB_DOCS: if hrefs[path].media_type not in OEB_DOCS or
not ref.title:
continue continue
buffer.write('<reference title="%s" type="%s" ' buffer.write('<reference title="%s" type="%s" '
% (ref.title, ref.type)) % (ref.title, ref.type))
@ -485,12 +487,14 @@ def main(argv=sys.argv):
fbase = context.dest.fbase fbase = context.dest.fbase
fkey = context.dest.fnums.values() fkey = context.dest.fnums.values()
tocadder = HTMLTOCAdder() tocadder = HTMLTOCAdder()
mangler = CaseMangler()
flattener = CSSFlattener( flattener = CSSFlattener(
fbase=fbase, fkey=fkey, unfloat=True, untable=True) fbase=fbase, fkey=fkey, unfloat=True, untable=True)
rasterizer = SVGRasterizer() rasterizer = SVGRasterizer()
trimmer = ManifestTrimmer() trimmer = ManifestTrimmer()
mobimlizer = MobiMLizer() mobimlizer = MobiMLizer()
tocadder.transform(oeb, context) tocadder.transform(oeb, context)
mangler.transform(oeb, context)
flattener.transform(oeb, context) flattener.transform(oeb, context)
rasterizer.transform(oeb, context) rasterizer.transform(oeb, context)
mobimlizer.transform(oeb, context) mobimlizer.transform(oeb, context)

View File

@ -66,6 +66,25 @@ OEB_RASTER_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME])
OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME]) OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME])
MS_COVER_TYPE = 'other.ms-coverimage-standard' MS_COVER_TYPE = 'other.ms-coverimage-standard'
GUIDE_TITLES = {
'cover': 'Cover',
'title-page': 'Title Page',
'toc': 'Table of Contents',
'index': 'Index',
'glossary': 'Glossary',
'acknowledgements': 'Acknowledgements',
'bibliography': 'Bibliography',
'colophon': 'Colophon',
'copyright-page': 'Copyright',
'dedication': 'Dedication',
'epigraph': 'Epigraph',
'foreword': 'Foreword',
'loi': 'List of Illustrations',
'lot': 'List of Tables',
'notes': 'Notes',
'preface': 'Preface',
'text': 'Begin Reading'
}
recode = lambda s: s.decode('iso-8859-1').encode('ascii', 'xmlcharrefreplace') recode = lambda s: s.decode('iso-8859-1').encode('ascii', 'xmlcharrefreplace')
ENTITYDEFS = dict((k, recode(v)) for k, v in htmlentitydefs.entitydefs.items()) ENTITYDEFS = dict((k, recode(v)) for k, v in htmlentitydefs.entitydefs.items())
@ -537,10 +556,12 @@ class Spine(object):
class Guide(object): class Guide(object):
class Reference(object): class Reference(object):
def __init__(self, type, title, href): def __init__(self, type, title, href):
if not title and type in GUIDE_TITLES:
title = GUIDE_TITLES[type]
self.type = type self.type = type
self.title = title self.title = title
self.href = urlnormalize(href) self.href = urlnormalize(href)
def __repr__(self): def __repr__(self):
return 'Reference(type=%r, title=%r, href=%r)' \ return 'Reference(type=%r, title=%r, href=%r)' \
% (self.type, self.title, self.href) % (self.type, self.title, self.href)

View File

@ -0,0 +1,111 @@
'''
CSS case-mangling transform.
'''
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
import sys
import os
import re
import operator
import math
from itertools import chain
from collections import defaultdict
from lxml import etree
from calibre.ebooks.oeb.base import XHTML, XHTML_NS
from calibre.ebooks.oeb.base import CSS_MIME
from calibre.ebooks.oeb.base import namespace
from calibre.ebooks.oeb.stylizer import Stylizer
CASE_MANGLER_CSS = """
.calibre_lowercase {
font-variant: normal;
font-size: 0.65em;
}
"""
TEXT_TRANSFORMS = set(['capitalize', 'uppercase', 'lowercase'])
class CaseMangler(object):
def transform(self, oeb, context):
oeb.logger.info('Applying case-transforming CSS...')
self.oeb = oeb
self.profile = context.source
self.mangle_spine()
def mangle_spine(self):
id, href = self.oeb.manifest.generate('manglecase', 'manglecase.css')
self.oeb.manifest.add(id, href, CSS_MIME, data=CASE_MANGLER_CSS)
for item in self.oeb.spine:
html = item.data
etree.SubElement(html.find(XHTML('head')), XHTML('link'),
rel='stylesheet', href=href, type=CSS_MIME)
stylizer = Stylizer(html, item.href, self.oeb, self.profile)
self.mangle_elem(html.find(XHTML('body')), stylizer)
def text_transform(self, transform, text):
if transform == 'capitalize':
return text.title()
elif transform == 'uppercase':
return text.upper()
elif transform == 'lowercase':
return text.lower()
return text
def split_text(self, text):
results = ['']
isupper = text[0].isupper()
for char in text:
if char.isupper() == isupper:
results[-1] += char
else:
isupper = not isupper
results.append(char)
return results
def smallcaps_elem(self, elem, attr):
texts = self.split_text(getattr(elem, attr))
setattr(elem, attr, None)
last = elem if attr == 'tail' else None
attrib = {'class': 'calibre_lowercase'}
for text in texts:
if text.isupper():
if last is None:
elem.text = text
else:
last.tail = text
else:
child = etree.Element(XHTML('span'), attrib=attrib)
child.text = text.upper()
if last is None:
elem.insert(0, child)
else:
# addnext() moves the tail for some reason
tail = last.tail
last.addnext(child)
last.tail = tail
child.tail = None
last = child
def mangle_elem(self, elem, stylizer):
if not isinstance(elem.tag, basestring) or \
namespace(elem.tag) != XHTML_NS:
return
children = list(elem)
style = stylizer.style(elem)
transform = style['text-transform']
variant = style['font-variant']
if elem.text:
if transform in TEXT_TRANSFORMS:
elem.text = self.text_transform(transform, elem.text)
if variant == 'small-caps':
self.smallcaps_elem(elem, 'text')
for child in children:
self.mangle_elem(child, stylizer)
if child.tail:
if transform in TEXT_TRANSFORMS:
child.tail = self.text_transform(transform, child.tail)
if variant == 'small-caps':
self.smallcaps_elem(child, 'tail')