mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use shorthand properties for margin, border and padding
This commit is contained in:
parent
c6491d03a6
commit
5789835550
@ -25,7 +25,8 @@ class OEBOutput(OutputFormatPlugin):
|
|||||||
self.log, self.opts = log, opts
|
self.log, self.opts = log, opts
|
||||||
if not os.path.exists(output_path):
|
if not os.path.exists(output_path):
|
||||||
os.makedirs(output_path)
|
os.makedirs(output_path)
|
||||||
from calibre.ebooks.oeb.base import OPF_MIME, NCX_MIME, PAGE_MAP_MIME
|
from calibre.ebooks.oeb.base import OPF_MIME, NCX_MIME, PAGE_MAP_MIME, OEB_STYLES
|
||||||
|
from calibre.ebooks.oeb.normalize_css import condense_sheet
|
||||||
with CurrentDir(output_path):
|
with CurrentDir(output_path):
|
||||||
results = oeb_book.to_opf2(page_map=True)
|
results = oeb_book.to_opf2(page_map=True)
|
||||||
for key in (OPF_MIME, NCX_MIME, PAGE_MAP_MIME):
|
for key in (OPF_MIME, NCX_MIME, PAGE_MAP_MIME):
|
||||||
@ -53,6 +54,8 @@ class OEBOutput(OutputFormatPlugin):
|
|||||||
f.write(raw)
|
f.write(raw)
|
||||||
|
|
||||||
for item in oeb_book.manifest:
|
for item in oeb_book.manifest:
|
||||||
|
if item.media_type in OEB_STYLES and hasattr(item.data, 'cssText'):
|
||||||
|
condense_sheet(item.data)
|
||||||
path = os.path.abspath(unquote(item.href))
|
path = os.path.abspath(unquote(item.href))
|
||||||
dir = os.path.dirname(path)
|
dir = os.path.dirname(path)
|
||||||
if not os.path.exists(dir):
|
if not os.path.exists(dir):
|
||||||
|
@ -22,6 +22,7 @@ from calibre.ebooks.mobi.utils import (create_text_record, to_base,
|
|||||||
from calibre.ebooks.compression.palmdoc import compress_doc
|
from calibre.ebooks.compression.palmdoc import compress_doc
|
||||||
from calibre.ebooks.oeb.base import (OEB_DOCS, OEB_STYLES, SVG_MIME, XPath,
|
from calibre.ebooks.oeb.base import (OEB_DOCS, OEB_STYLES, SVG_MIME, XPath,
|
||||||
extract, XHTML, urlnormalize)
|
extract, XHTML, urlnormalize)
|
||||||
|
from calibre.ebooks.oeb.normalize_css import condense_sheet
|
||||||
from calibre.ebooks.oeb.parse_utils import barename
|
from calibre.ebooks.oeb.parse_utils import barename
|
||||||
from calibre.ebooks.mobi.writer8.skeleton import Chunker, aid_able_tags, to_href
|
from calibre.ebooks.mobi.writer8.skeleton import Chunker, aid_able_tags, to_href
|
||||||
from calibre.ebooks.mobi.writer8.index import (NCXIndex, SkelIndex,
|
from calibre.ebooks.mobi.writer8.index import (NCXIndex, SkelIndex,
|
||||||
@ -150,6 +151,8 @@ class KF8Writer(object):
|
|||||||
|
|
||||||
for item in self.oeb.manifest:
|
for item in self.oeb.manifest:
|
||||||
if item.media_type in OEB_STYLES:
|
if item.media_type in OEB_STYLES:
|
||||||
|
if hasattr(item.data, 'cssText'):
|
||||||
|
condense_sheet(self.data(item))
|
||||||
data = self.data(item).cssText
|
data = self.data(item).cssText
|
||||||
sheets[item.href] = len(self.flows)
|
sheets[item.href] = len(self.flows)
|
||||||
self.flows.append(force_unicode(data, 'utf-8'))
|
self.flows.append(force_unicode(data, 'utf-8'))
|
||||||
|
@ -57,6 +57,7 @@ DEFAULTS = {'azimuth': 'center', 'background-attachment': 'scroll', # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
EDGES = ('top', 'right', 'bottom', 'left')
|
EDGES = ('top', 'right', 'bottom', 'left')
|
||||||
|
BORDER_PROPS = ('color', 'style', 'width')
|
||||||
|
|
||||||
def normalize_edge(name, cssvalue):
|
def normalize_edge(name, cssvalue):
|
||||||
style = {}
|
style = {}
|
||||||
@ -162,11 +163,85 @@ for x in ('margin', 'padding', 'border-style', 'border-width', 'border-color'):
|
|||||||
|
|
||||||
for x in EDGES:
|
for x in EDGES:
|
||||||
name = 'border-' + x
|
name = 'border-' + x
|
||||||
normalizers[name] = simple_normalizer(name, ('color', 'style', 'width'), check_inherit=False)
|
normalizers[name] = simple_normalizer(name, BORDER_PROPS, check_inherit=False)
|
||||||
|
|
||||||
|
def condense_edge(vals):
|
||||||
|
edges = {x.name.rpartition('-')[-1]:x.value for x in vals}
|
||||||
|
if len(edges) != 4:
|
||||||
|
return
|
||||||
|
ce = {}
|
||||||
|
for (x, y) in [('left', 'right'), ('top', 'bottom')]:
|
||||||
|
if edges[x] == edges[y]:
|
||||||
|
ce[x] = edges[x]
|
||||||
|
else:
|
||||||
|
ce[x], ce[y] = edges[x], edges[y]
|
||||||
|
if len(ce) == 4:
|
||||||
|
return ' '.join(ce[x] for x in ('top', 'right', 'bottom', 'left'))
|
||||||
|
if len(ce) == 3:
|
||||||
|
if 'right' in ce:
|
||||||
|
return ' '.join(ce[x] for x in ('top', 'right', 'top', 'left'))
|
||||||
|
return ' '.join(ce[x] for x in ('top', 'left', 'bottom'))
|
||||||
|
if len(ce) == 2:
|
||||||
|
if ce['top'] == ce['left']:
|
||||||
|
return ce['top']
|
||||||
|
return ' '.join(ce[x] for x in ('top', 'left'))
|
||||||
|
|
||||||
|
def simple_condenser(prefix, func):
|
||||||
|
@wraps(func)
|
||||||
|
def condense_simple(style, props):
|
||||||
|
cp = func(props)
|
||||||
|
if cp is not None:
|
||||||
|
for prop in props:
|
||||||
|
style.removeProperty(prop.name)
|
||||||
|
style.setProperty(prefix, cp)
|
||||||
|
return condense_simple
|
||||||
|
|
||||||
|
def condense_border(style, props):
|
||||||
|
prop_map = {p.name:p for p in props}
|
||||||
|
edge_vals = []
|
||||||
|
for edge in EDGES:
|
||||||
|
name = 'border-%s' % edge
|
||||||
|
vals = []
|
||||||
|
for prop in BORDER_PROPS:
|
||||||
|
x = prop_map.get('%s-%s' % (name, prop), None)
|
||||||
|
if x is not None:
|
||||||
|
vals.append(x)
|
||||||
|
if len(vals) == 3:
|
||||||
|
for prop in vals:
|
||||||
|
style.removeProperty(prop.name)
|
||||||
|
style.setProperty(name, ' '.join(x.value for x in vals))
|
||||||
|
prop_map[name] = style.getProperty(name)
|
||||||
|
x = prop_map.get(name, None)
|
||||||
|
if x is not None:
|
||||||
|
edge_vals.append(x)
|
||||||
|
if len(edge_vals) == 4 and len({x.value for x in edge_vals}) == 1:
|
||||||
|
for prop in edge_vals:
|
||||||
|
style.removeProperty(prop.name)
|
||||||
|
style.setProperty('border', edge_vals[0].value)
|
||||||
|
|
||||||
|
condensers = {'margin': simple_condenser('margin', condense_edge), 'padding': simple_condenser('padding', condense_edge), 'border': condense_border}
|
||||||
|
|
||||||
|
|
||||||
|
def condense_rule(style):
|
||||||
|
expanded = {'margin-':[], 'padding-':[], 'border-':[]}
|
||||||
|
for prop in style.getProperties():
|
||||||
|
for x in expanded:
|
||||||
|
if prop.name and prop.name.startswith(x):
|
||||||
|
expanded[x].append(prop)
|
||||||
|
break
|
||||||
|
for prefix, vals in expanded.iteritems():
|
||||||
|
if len(vals) > 1 and {x.priority for x in vals} == {''}:
|
||||||
|
condensers[prefix[:-1]](style, vals)
|
||||||
|
|
||||||
|
def condense_sheet(sheet):
|
||||||
|
for rule in sheet.cssRules:
|
||||||
|
if rule.type == rule.STYLE_RULE:
|
||||||
|
condense_rule(rule.style)
|
||||||
|
|
||||||
def test_normalization(): # {{{
|
def test_normalization(): # {{{
|
||||||
import unittest
|
import unittest
|
||||||
from cssutils import parseStyle
|
from cssutils import parseStyle
|
||||||
|
from itertools import product
|
||||||
|
|
||||||
class TestNormalization(unittest.TestCase):
|
class TestNormalization(unittest.TestCase):
|
||||||
longMessage = True
|
longMessage = True
|
||||||
@ -262,6 +337,50 @@ def test_normalization(): # {{{
|
|||||||
cval = tuple(parseStyle('list-style: %s' % raw, validate=False))[0].cssValue
|
cval = tuple(parseStyle('list-style: %s' % raw, validate=False))[0].cssValue
|
||||||
self.assertDictEqual(ls_dict(expected), normalizers['list-style']('list-style', cval))
|
self.assertDictEqual(ls_dict(expected), normalizers['list-style']('list-style', cval))
|
||||||
|
|
||||||
|
def test_edge_condensation(self):
|
||||||
|
for s, v in {
|
||||||
|
(1, 1, 3) : None,
|
||||||
|
(1, 2, 3, 4) : '2pt 3pt 4pt 1pt',
|
||||||
|
(1, 2, 3, 2) : '2pt 3pt 2pt 1pt',
|
||||||
|
(1, 2, 1, 3) : '2pt 1pt 3pt',
|
||||||
|
(1, 2, 1, 2) : '2pt 1pt',
|
||||||
|
(1, 1, 1, 1) : '1pt',
|
||||||
|
('2%', '2%', '2%', '2%') : '2%',
|
||||||
|
tuple('0 0 0 0'.split()) : '0',
|
||||||
|
}.iteritems():
|
||||||
|
for prefix in ('margin', 'padding'):
|
||||||
|
css = {'%s-%s' % (prefix, x) : str(y)+'pt' if isinstance(y, (int, float)) else y for x, y in zip(('left', 'top', 'right', 'bottom'), s)}
|
||||||
|
css = '; '.join(('%s:%s' % (k, v) for k, v in css.iteritems()))
|
||||||
|
style = parseStyle(css)
|
||||||
|
condense_rule(style)
|
||||||
|
val = getattr(style.getProperty(prefix), 'value', None)
|
||||||
|
self.assertEqual(v, val)
|
||||||
|
if val is not None:
|
||||||
|
for edge in EDGES:
|
||||||
|
self.assertFalse(getattr(style.getProperty('%s-%s' % (prefix, edge)), 'value', None))
|
||||||
|
|
||||||
|
def test_border_condensation(self):
|
||||||
|
vals = 'red solid 5px'
|
||||||
|
css = '; '.join('border-%s-%s: %s' % (edge, p, v) for edge in EDGES for p, v in zip(BORDER_PROPS, vals.split()))
|
||||||
|
style = parseStyle(css)
|
||||||
|
condense_rule(style)
|
||||||
|
for e, p in product(EDGES, BORDER_PROPS):
|
||||||
|
self.assertFalse(style.getProperty('border-%s-%s' % (e, p)))
|
||||||
|
self.assertFalse(style.getProperty('border-%s' % e))
|
||||||
|
self.assertFalse(style.getProperty('border-%s' % p))
|
||||||
|
self.assertEqual(style.getProperty('border').value, vals)
|
||||||
|
css = '; '.join('border-%s-%s: %s' % (edge, p, v) for edge in ('top',) for p, v in zip(BORDER_PROPS, vals.split()))
|
||||||
|
style = parseStyle(css)
|
||||||
|
condense_rule(style)
|
||||||
|
self.assertEqual(style.cssText, 'border-top: %s' % vals)
|
||||||
|
css += ';' + '; '.join('border-%s-%s: %s' % (edge, p, v) for edge in ('right', 'left', 'bottom') for p, v in
|
||||||
|
zip(BORDER_PROPS, vals.replace('red', 'green').split()))
|
||||||
|
style = parseStyle(css)
|
||||||
|
condense_rule(style)
|
||||||
|
self.assertEqual(len(style.getProperties()), 4)
|
||||||
|
self.assertEqual(style.getProperty('border-top').value, vals)
|
||||||
|
self.assertEqual(style.getProperty('border-left').value, vals.replace('red', 'green'))
|
||||||
|
|
||||||
tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestNormalization)
|
tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestNormalization)
|
||||||
unittest.TextTestRunner(verbosity=4).run(tests)
|
unittest.TextTestRunner(verbosity=4).run(tests)
|
||||||
# }}}
|
# }}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user