1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 16:37:47 +03:00

Support presentational hints

This commit is contained in:
Guillaume Ayoub 2016-08-29 17:50:07 +02:00
parent c5c06e6c01
commit 49f7a4acec

View File

@ -202,6 +202,13 @@ def find_stylesheets(element_tree, device_media_type, url_fetcher):
href, exc)
def check_style_attribute(parser, element, style_attribute):
declarations, errors = parser.parse_style_attr(style_attribute)
for error in errors:
LOGGER.warning(error)
return element, declarations, element_base_url(element)
def find_style_attributes(element_tree):
"""
Yield ``element, declaration, base_url`` for elements with
@ -211,10 +218,363 @@ def find_style_attributes(element_tree):
for element in element_tree.iter():
style_attribute = element.get('style')
if style_attribute:
declarations, errors = parser.parse_style_attr(style_attribute)
for error in errors:
LOGGER.warning(error)
yield element, declarations, element_base_url(element)
yield check_style_attribute(parser, element, style_attribute)
def find_presentational_hints(element_tree):
"""
Yield ``element, declaration, base_url`` for elements with
presentational hints.
"""
parser = PARSER
for element in element_tree.iter():
if element.tag == 'body':
for prop, position in (
('height', 'top'), ('height', 'bottom'),
('width', 'left'), ('width', 'right')):
style_attribute = None
for prop in ('margin%s' % prop, '%smargin' % prop):
if element.get(prop):
style_attribute = 'margin-%s:%spx' % (
position, element.get('margin%r' % prop))
break
if style_attribute:
yield check_style_attribute(
parser, element, style_attribute)
if element.get('background'):
style_attribute = 'background-image:%s' % element.get('background')
yield check_style_attribute(parser, element, style_attribute)
if element.get('bgcolor'):
style_attribute = 'background-color:%s' % element.get('bgcolor')
yield check_style_attribute(parser, element, style_attribute)
if element.get('text'):
style_attribute = 'color:%s' % element.get('text')
yield check_style_attribute(parser, element, style_attribute)
elif element.tag == 'pre':
if 'wrap' in element:
yield check_style_attribute(
parser, element, 'white-space:pre-wrap')
elif element.tag == 'center':
yield check_style_attribute(parser, element, 'text-align:center')
elif element.tag == 'div':
align = element.get('align', '').lower()
if align == 'middle':
yield check_style_attribute(
parser, element, 'text-align:center')
elif align in ('center', 'left', 'right', 'justify'):
yield check_style_attribute(
parser, element, 'text-align:%s' % align)
elif element.tag == 'br':
clear = element.get('clear', '').lower()
if clear:
if clear == 'left':
yield check_style_attribute(parser, element, 'clear:left')
elif clear == 'right':
yield check_style_attribute(parser, element, 'clear:right')
elif clear in ('all', 'both'):
yield check_style_attribute(parser, element, 'clear:both')
elif element.tag == 'font':
if element.get('color'):
yield check_style_attribute(
parser, element, 'color:%s' % element.get('color'))
if element.get('face'):
yield check_style_attribute(
parser, element, 'font-family:%s' % element.get('face'))
if element.get('size'):
size = element.get('size').strip()
relative_plus = size.startswith('+')
relative_minus = size.startswith('-')
if relative_plus or relative_minus:
size = size[1:].strip()
try:
size = int(size)
except ValueError:
LOGGER.warning('Invalid value for size: %s' % size)
else:
font_sizes = {
1: 'x-small',
2: 'small',
3: 'medium',
4: 'large',
5: 'x-large',
6: 'xx-large',
7: '48px',
}
if relative_plus:
size += 3
elif relative_minus:
size -= 3
size = min(1, max(7, size))
yield check_style_attribute(
parser, element, 'font-size:%s' % font_sizes[size])
elif element.tag in ('ol', 'ul', 'li'):
if element.get('type'):
if element.tag in ('ol', 'li'):
if element.get('type') == '1':
yield check_style_attribute(
parser, element, 'list-style-type:decimal')
elif element.get('type') == 'a':
yield check_style_attribute(
parser, element, 'list-style-type:lower-alpha')
elif element.get('type') == 'A':
yield check_style_attribute(
parser, element, 'list-style-type:upper-alpha')
elif element.get('type') == 'i':
yield check_style_attribute(
parser, element, 'list-style-type:lower-roman')
elif element.get('type') == 'I':
yield check_style_attribute(
parser, element, 'list-style-type:upper-roman')
elif element.tag in ('ul', 'li'):
if element.get('type').lower() in ('disc', 'circle', 'square'):
yield check_style_attribute(
parser, element,
'list-style-type:%s' % element.get('type').lower())
elif element.tag == 'table':
# TODO: Rules for children are not handled
align = element.get('align', '').lower()
if align == 'left':
yield check_style_attribute(parser, element, 'float:left')
elif align == 'right':
yield check_style_attribute(parser, element, 'float:right')
elif align == 'center':
yield check_style_attribute(
parser, element, 'margin-left:auto;margin-right:auto')
rules = element.get('rules', '').lower()
if rules in ('none', 'groups', 'rows', 'cols', 'all'):
yield check_style_attribute(
parser, element,
'border-style:hidden;border-collapse:collapse')
if 'border' in element:
yield check_style_attribute(
parser, element, 'border-style:outset')
frame = element.get('frame', '').lower()
if frame == 'void':
yield check_style_attribute(
parser, element, 'border-style:hidden')
elif frame == 'above':
yield check_style_attribute(
parser, element,
'border-style:outset hidden hidden hidden')
elif frame == 'below':
yield check_style_attribute(
parser, element,
'border-style:hidden hidden outset hidden')
elif frame == 'hsides':
yield check_style_attribute(
parser, element,
'border-style:outset hidden outset hidden')
elif frame == 'lhs':
yield check_style_attribute(
parser, element,
'border-style:hidden hidden hidden outset')
elif frame == 'rhs':
yield check_style_attribute(
parser, element,
'border-style:hidden outset hidden hidden')
elif frame == 'vslides':
yield check_style_attribute(
parser, element,
'border-style:hidden outset')
elif frame in ('box', 'border'):
yield check_style_attribute(
parser, element, 'border-style:outset')
if element.get('cellspacing'):
yield check_style_attribute(
parser, element,
'border-spacing:%spx' % element.get('cellspacing'))
if element.get('hspace'):
hspace = element.get('hspace')
if hspace.isdigit():
hspace += 'px'
yield check_style_attribute(
parser, element,
'margin-left:%s;margin-right:%s' % (hspace, hspace))
if element.get('vspace'):
vspace = element.get('vspace')
if vspace.isdigit():
vspace += 'px'
yield check_style_attribute(
parser, element,
'margin-top:%s;margin-bottom:%s' % (vspace, vspace))
if element.get('width'):
style_attribute = 'width:%s' % element.get('width')
if element.get('width').isdigit():
style_attribute += 'px'
yield check_style_attribute(parser, element, style_attribute)
if element.get('height'):
style_attribute = 'height:%s' % element.get('height')
if element.get('height').isdigit():
style_attribute += 'px'
yield check_style_attribute(parser, element, style_attribute)
if element.get('background'):
style_attribute = 'background-image:%s' % (
element.get('background'))
yield check_style_attribute(parser, element, style_attribute)
if element.get('bgcolor'):
style_attribute = 'background-color:%s' % (
element.get('bgcolor'))
yield check_style_attribute(parser, element, style_attribute)
if element.get('bordercolor'):
style_attribute = 'border-color:%s' % (
element.get('bordercolor'))
yield check_style_attribute(parser, element, style_attribute)
if element.get('border'):
style_attribute = 'border-width:%spx' % (
element.get('border'))
yield check_style_attribute(parser, element, style_attribute)
elif element.tag in ('tr', 'td', 'th', 'thead', 'tbody', 'tfoot'):
align = element.get('align', '').lower()
if align == 'absmiddle':
yield check_style_attribute(
parser, element, 'text-align:center')
if align in ('left', 'right', 'justify'):
yield check_style_attribute(
parser, element, 'text-align:%s' % align)
valign = element.get('valign', '').lower()
if valign in ('top', 'middle', 'bottom', 'baseline'):
yield check_style_attribute(
parser, element, 'vertical-align:%s' % valign)
if element.tag in ('td', 'th'):
if 'nowrap' in element:
yield check_style_attribute(
parser, element, 'white-space:nowrap')
if element.get('cellpadding'):
yield check_style_attribute(
parser, element,
'padding:%spx' % element.get('cellpadding'))
if element.get('width'):
style_attribute = 'width:%s' % element.get('width')
if element.get('width').isdigit():
style_attribute += 'px'
yield check_style_attribute(
parser, element, style_attribute)
if element.tag in ('tr', 'td', 'th'):
if element.get('height'):
style_attribute = 'height:%s' % element.get('height')
if element.get('height').isdigit():
style_attribute += 'px'
yield check_style_attribute(
parser, element, style_attribute)
if element.get('background'):
style_attribute = 'background-image:%s' % (
element.get('background'))
yield check_style_attribute(parser, element, style_attribute)
if element.get('bgcolor'):
style_attribute = 'background-color:%s' % (
element.get('bgcolor'))
yield check_style_attribute(parser, element, style_attribute)
elif element.tag == 'caption':
align = element.get('align', '').lower()
if align == 'bottom':
yield check_style_attribute(
parser, element, 'caption-side:bottom')
if align in ('left', 'right', 'justify'):
yield check_style_attribute(
parser, element, 'text-align:%s' % align)
elif element.tag == 'col':
if element.get('width'):
style_attribute = 'width:%s' % element.get('width')
if element.get('width').isdigit():
style_attribute += 'px'
yield check_style_attribute(parser, element, style_attribute)
elif element.tag in ('p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'):
align = element.get('align', '').lower()
if align in ('center', 'left', 'right', 'justify'):
yield check_style_attribute(
parser, element, 'text-align:%s' % align)
elif element.tag == 'hr':
align = element.get('align', '').lower()
if align == 'left':
yield check_style_attribute(
parser, element, 'margin-left:0;margin-right:auto')
elif align == 'right':
yield check_style_attribute(
parser, element, 'margin-left:auto;margin-right:0')
elif align == 'center':
yield check_style_attribute(
parser, element, 'margin-left:auto;margin-right:auto')
size = 0
if element.get('size'):
try:
size = int(element.get('size'))
except ValueError:
LOGGER.warning('Invalid value for size: %s' % size)
if 'color' in element or 'noshade' in element:
yield check_style_attribute(
parser, element, 'border-style:solid')
if size >= 1:
yield check_style_attribute(
parser, element, 'border-width:%spx' % size / 2)
elif size == 1:
yield check_style_attribute(
parser, element, 'border-bottom-width:0')
elif size > 1:
yield check_style_attribute(
parser, element, 'height:%spx' % (size - 2))
if element.get('width'):
style_attribute = 'width:%s' % element.get('width')
if element.get('width').isdigit():
style_attribute += 'px'
yield check_style_attribute(parser, element, style_attribute)
if element.get('color'):
yield check_style_attribute(
parser, element, 'color:%s' % element.get('color'))
elif element.tag in (
'iframe', 'applet', 'embed', 'img', 'input', 'object'):
if (element.tag != 'input' or
element.get('type', '').lower() == 'image'):
align = element.get('align', '').lower()
if align in ('left', 'right'):
yield check_style_attribute(
parser, element, 'float:%s' % align)
elif align in ('top', 'baseline', 'bottom'):
yield check_style_attribute(
parser, element, 'vertical-align:%s' % align)
elif align == 'texttop':
yield check_style_attribute(
parser, element, 'vertical-align:text-top')
elif align in ('absmiddle', 'abscenter', 'middle', 'center'):
# TODO: middle and center values are wrong
yield check_style_attribute(
parser, element, 'vertical-align:middle')
if element.get('hspace'):
hspace = element.get('hspace')
if hspace.isdigit():
hspace += 'px'
yield check_style_attribute(
parser, element,
'margin-left:%s;margin-right:%s' % (hspace, hspace))
if element.get('vspace'):
vspace = element.get('vspace')
if vspace.isdigit():
vspace += 'px'
yield check_style_attribute(
parser, element,
'margin-top:%s;margin-bottom:%s' % (vspace, vspace))
# TODO: img seems to be excluded for width and height, but a
# lot of W3C tests rely on this attribute being applied to img
if element.get('width'):
style_attribute = 'width:%s' % element.get('width')
if element.get('width').isdigit():
style_attribute += 'px'
yield check_style_attribute(
parser, element, style_attribute)
if element.get('height'):
style_attribute = 'height:%s' % element.get('height')
if element.get('height').isdigit():
style_attribute += 'px'
yield check_style_attribute(
parser, element, style_attribute)
if element.tag in ('img', 'object', 'input'):
if element.get('border'):
yield check_style_attribute(
parser, element,
'border-width:%spx;border-style:solid' %
element.get('border'))
if element.tag == 'iframe':
if element.get('frameborder', '').lower() in ('0', 'no'):
yield check_style_attribute(parser, element, 'border:none')
def evaluate_media_query(query_list, device_media_type):
@ -485,6 +845,15 @@ def get_all_computed_styles(html, user_stylesheets=None):
cascaded_styles, name, values, weight,
element, pseudo_type)
specificity = (0, 0, 0, 0)
for element, declarations, base_url in find_presentational_hints(
element_tree):
for name, values, importance in preprocess_declarations(
base_url, declarations):
precedence = declaration_precedence('author', importance)
weight = (precedence, specificity)
add_declaration(cascaded_styles, name, values, weight, element)
specificity = (1, 0, 0, 0)
for element, declarations, base_url in find_style_attributes(element_tree):
for name, values, importance in preprocess_declarations(