Add an option to capitalize margin and float properties

The option is disabled by default, that weird idea comes from a weird
outlook.com behaviour to handle margin and float properties only if the
first letter is uppercase.

See https://www.emailonacid.com/blog/article/email-development/outlook.com-does-support-margins
This commit is contained in:
Jocelyn Delalande 2015-10-20 11:40:49 +02:00
parent 50d0719ce6
commit 27ac936a22
5 changed files with 93 additions and 6 deletions

View File

@ -64,6 +64,12 @@ def main(args):
action="store_true", dest="remove_classes"
)
parser.add_argument(
"--capitalize-float-margin", default=False,
help="Capitalize float and margin properties for outlook.com compat.",
action="store_true", dest="capitalize_float_margin"
)
parser.add_argument(
"--strip-important", default=False,
help="Remove '!important' for all css declarations.",

View File

@ -85,8 +85,27 @@ def _cache_parse_css_string(css_body, validate=True):
"""
return cssutils.parseString(css_body, validate=validate)
def capitalize_float_margin(css_body):
"""Capitalize float and margin CSS property names
"""
def _capitalize_property(match):
return '{0}:{1}{2}'.format(
match.group('property').capitalize(),
match.group('value'),
match.group('terminator'))
return _lowercase_margin_float_rule.sub(_capitalize_property, css_body)
_element_selector_regex = re.compile(r'(^|\s)\w')
_cdata_regex = re.compile(r'\<\!\[CDATA\[(.*?)\]\]\>', re.DOTALL)
_lowercase_margin_float_rule = re.compile(
r'''(?P<property>margin(-(top|bottom|left|right))?|float)
:
(?P<value>.*?)
(?P<terminator>$|;)''',
re.IGNORECASE | re.VERBOSE)
_importants = re.compile('\s*!important')
# These selectors don't apply to all elements. Rather, they specify
# which elements to apply to.
@ -104,6 +123,7 @@ class Premailer(object):
keep_style_tags=False,
include_star_selectors=False,
remove_classes=True,
capitalize_float_margin=False,
strip_important=True,
external_styles=None,
css_text=None,
@ -126,6 +146,7 @@ class Premailer(object):
# this will always preserve the original css
self.keep_style_tags = keep_style_tags
self.remove_classes = remove_classes
self.capitalize_float_margin = capitalize_float_margin
# whether to process or ignore selectors like '* { foo:bar; }'
self.include_star_selectors = include_star_selectors
if isinstance(external_styles, STR_TYPE):
@ -420,6 +441,14 @@ class Premailer(object):
parent = item.getparent()
del parent.attrib['class']
# Capitalize Margin properties
# To fix weird outlook bug
# https://www.emailonacid.com/blog/article/email-development/outlook.com-does-support-margins
if self.capitalize_float_margin:
for item in page.xpath('//@style'):
mangled = capitalize_float_margin(item)
item.getparent().attrib['style'] = mangled
# Add align attributes to images if they have a CSS float value of
# right or left. Outlook (both on desktop and on the web) are bad at
# understanding floats, but they do understand the HTML align attrib.

View File

@ -16,9 +16,3 @@ class TestMergeStyle(unittest.TestCase):
# Invalid syntax does not raise
inline = '{color:pink} :hover{color:purple} :active{color:red}'
merge_styles(inline, [], [])
def test_important_rule(self):
# No exception after #133
csstext = 'font-size:1px !important'
parsed_csstext = csstext_to_pairs(csstext)
self.assertEqual(('font-size', '1px'), parsed_csstext[0])

View File

@ -828,6 +828,45 @@ ical-align:middle" bgcolor="red" valign="middle">Cell 2</td>
compare_html(expect_html, result_html)
def test_uppercase_margin(self):
"""Option to comply with outlook.com
https://emailonacid.com/blog/article/email-development/outlook.com-does-support-margins
"""
html = """<html>
<head>
<title>Title</title>
</head>
<style>
h1 {margin: 0}
h2 {margin-top:0;margin-bottom:0;margin-left:0;margin-right:0}
</style>
<body>
<h1>a</h1>
<h2>
b
</h2>
</body>
</html>"""
expect_html = """<html>
<head>
<title>Title</title>
</head>
<body>
<h1 style="Margin:0">a</h1>
<h2 style="Margin-bottom:0; Margin-left:0; Margin-right:0; Margin-top:0">
b
</h2>
</body>
</html>"""
p = Premailer(html, capitalize_float_margin=True)
result_html = p.transform()
compare_html(expect_html, result_html)
def test_strip_important(self):
"""Get rid of !important. Makes no sense inline."""
html = """<html>

View File

@ -0,0 +1,19 @@
import unittest
from premailer.premailer import capitalize_float_margin
class UtilsTestCase(unittest.TestCase):
def testcapitalize_float_margin(self):
self.assertEqual(
capitalize_float_margin('margin:1em'),
'Margin:1em')
self.assertEqual(
capitalize_float_margin('margin-left:1em'),
'Margin-left:1em')
self.assertEqual(
capitalize_float_margin('float:right;'),
'Float:right;')
self.assertEqual(
capitalize_float_margin('float:right;color:red;margin:0'),
'Float:right;color:red;Margin:0')