mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-12-27 23:30:20 -05:00
573 lines
28 KiB
Python
573 lines
28 KiB
Python
"""CSS profiles.
|
|
|
|
Profiles is based on code by Kevin D. Smith, orginally used as cssvalues,
|
|
thanks!
|
|
"""
|
|
__all__ = ['Profiles']
|
|
__docformat__ = 'restructuredtext'
|
|
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
|
|
|
|
import re
|
|
|
|
class NoSuchProfileException(Exception):
|
|
"""Raised if no profile with given name is found"""
|
|
pass
|
|
|
|
|
|
class Profiles(object):
|
|
"""
|
|
All profiles used for validation. ``cssutils.profile`` is a
|
|
preset object of this class and used by all properties for validation.
|
|
|
|
Predefined profiles are (use
|
|
:meth:`~cssutils.profiles.Profiles.propertiesByProfile` to
|
|
get a list of defined properties):
|
|
|
|
:attr:`~cssutils.profiles.Profiles.CSS_LEVEL_2`
|
|
Properties defined by CSS2.1
|
|
:attr:`~cssutils.profiles.Profiles.CSS3_COLOR`
|
|
CSS 3 color properties
|
|
:attr:`~cssutils.profiles.Profiles.CSS3_BOX`
|
|
Currently overflow related properties only
|
|
:attr:`~cssutils.profiles.Profiles.CSS3_PAGED_MEDIA`
|
|
As defined at http://www.w3.org/TR/css3-page/ (at 090307)
|
|
|
|
Predefined macros are:
|
|
|
|
:attr:`~cssutils.profiles.Profiles._TOKEN_MACROS`
|
|
Macros containing the token values as defined to CSS2
|
|
:attr:`~cssutils.profiles.Profiles._MACROS`
|
|
Additional general macros.
|
|
|
|
If you want to redefine any of these macros do this in your custom
|
|
macros.
|
|
"""
|
|
CSS_LEVEL_2 = 'CSS Level 2.1'
|
|
CSS3_BOX = CSS_BOX_LEVEL_3 = 'CSS Box Module Level 3'
|
|
CSS3_COLOR = CSS_COLOR_LEVEL_3 = 'CSS Color Module Level 3'
|
|
CSS3_FONTS = 'CSS Fonts Module Level 3'
|
|
CSS3_FONT_FACE = 'CSS Fonts Module Level 3 @font-face properties'
|
|
CSS3_PAGED_MEDIA = 'CSS3 Paged Media Module'
|
|
|
|
_TOKEN_MACROS = {
|
|
'ident': r'[-]?{nmstart}{nmchar}*',
|
|
'name': r'{nmchar}+',
|
|
'nmstart': r'[_a-z]|{nonascii}|{escape}',
|
|
'nonascii': r'[^\0-\177]',
|
|
'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
|
|
'escape': r'{unicode}|\\[ -~\200-\777]',
|
|
# 'escape': r'{unicode}|\\[ -~\200-\4177777]',
|
|
'int': r'[-]?\d+',
|
|
'nmchar': r'[\w-]|{nonascii}|{escape}',
|
|
'num': r'[-]?\d+|[-]?\d*\.\d+',
|
|
'positivenum': r'\d+|[-]?\d*\.\d+',
|
|
'number': r'{num}',
|
|
'string': r'{string1}|{string2}',
|
|
'string1': r'"(\\\"|[^\"])*"',
|
|
'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
|
|
'string2': r"'(\\\'|[^\'])*'",
|
|
'nl': r'\n|\r\n|\r|\f',
|
|
'w': r'\s*',
|
|
}
|
|
_MACROS = {
|
|
'hexcolor': r'#[0-9a-f]{3}|#[0-9a-f]{6}',
|
|
'rgbcolor': r'rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
|
|
'namedcolor': r'(transparent|orange|maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray)',
|
|
'uicolor': r'(ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)',
|
|
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{uicolor}',
|
|
#'color': r'(maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray|ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)|#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
|
|
'integer': r'{int}',
|
|
'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
|
|
'positivelength': r'0|{positivenum}(em|ex|px|in|cm|mm|pt|pc)',
|
|
'angle': r'0|{num}(deg|grad|rad)',
|
|
'time': r'0|{num}m?s',
|
|
'frequency': r'0|{num}k?Hz',
|
|
'percentage': r'{num}%',
|
|
}
|
|
|
|
def __init__(self, log=None):
|
|
"""A few known profiles are predefined."""
|
|
self._log = log
|
|
self._profileNames = [] # to keep order, REFACTOR!
|
|
self._profiles = {}
|
|
self._defaultProfiles = None
|
|
|
|
self.addProfile(self.CSS_LEVEL_2,
|
|
properties[self.CSS_LEVEL_2],
|
|
macros[self.CSS_LEVEL_2])
|
|
self.addProfile(self.CSS3_BOX,
|
|
properties[self.CSS3_BOX],
|
|
macros[self.CSS3_BOX])
|
|
self.addProfile(self.CSS3_COLOR,
|
|
properties[self.CSS3_COLOR],
|
|
macros[self.CSS3_COLOR])
|
|
|
|
self.addProfile(self.CSS3_FONTS,
|
|
properties[self.CSS3_FONTS],
|
|
macros[self.CSS3_FONTS])
|
|
|
|
# new object for font-face only?
|
|
self.addProfile(self.CSS3_FONT_FACE,
|
|
properties[self.CSS3_FONT_FACE],
|
|
macros[self.CSS3_FONTS]) # same
|
|
|
|
self.addProfile(self.CSS3_PAGED_MEDIA,
|
|
properties[self.CSS3_PAGED_MEDIA],
|
|
macros[self.CSS3_PAGED_MEDIA])
|
|
|
|
self.__update_knownNames()
|
|
|
|
def _expand_macros(self, dictionary, macros):
|
|
"""Expand macros in token dictionary"""
|
|
def macro_value(m):
|
|
return '(?:%s)' % macros[m.groupdict()['macro']]
|
|
for key, value in dictionary.items():
|
|
if not hasattr(value, '__call__'):
|
|
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
|
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
|
|
macro_value, value)
|
|
dictionary[key] = value
|
|
return dictionary
|
|
|
|
def _compile_regexes(self, dictionary):
|
|
"""Compile all regular expressions into callable objects"""
|
|
for key, value in dictionary.items():
|
|
if not hasattr(value, '__call__'):
|
|
value = re.compile('^(?:%s)$' % value, re.I).match
|
|
dictionary[key] = value
|
|
|
|
return dictionary
|
|
|
|
def __update_knownNames(self):
|
|
self._knownNames = []
|
|
for properties in self._profiles.values():
|
|
self._knownNames.extend(properties.keys())
|
|
|
|
def _getDefaultProfiles(self):
|
|
"If not explicitly set same as Profiles.profiles but in reverse order."
|
|
if not self._defaultProfiles:
|
|
return self.profiles
|
|
else:
|
|
return self._defaultProfiles
|
|
|
|
def _setDefaultProfiles(self, profiles):
|
|
"profiles may be a single or a list of profile names"
|
|
if isinstance(profiles, basestring):
|
|
self._defaultProfiles = (profiles,)
|
|
else:
|
|
self._defaultProfiles = profiles
|
|
|
|
defaultProfiles = property(_getDefaultProfiles,
|
|
_setDefaultProfiles,
|
|
doc=u"Names of profiles to use for validation."
|
|
u"To use e.g. the CSS2 profile set "
|
|
u"``cssutils.profile.defaultProfiles = "
|
|
u"cssutils.profile.CSS_LEVEL_2``")
|
|
|
|
profiles = property(lambda self: self._profileNames,
|
|
doc=u'Names of all profiles in order as defined.')
|
|
|
|
knownNames = property(lambda self: self._knownNames,
|
|
doc="All known property names of all profiles.")
|
|
|
|
def addProfile(self, profile, properties, macros=None):
|
|
"""Add a new profile with name `profile` (e.g. 'CSS level 2')
|
|
and the given `properties`.
|
|
|
|
:param profile:
|
|
the new `profile`'s name
|
|
:param properties:
|
|
a dictionary of ``{ property-name: propery-value }`` items where
|
|
property-value is a regex which may use macros defined in given
|
|
``macros`` or the standard macros Profiles.tokens and
|
|
Profiles.generalvalues.
|
|
|
|
``propery-value`` may also be a function which takes a single
|
|
argument which is the value to validate and which should return
|
|
True or False.
|
|
Any exceptions which may be raised during this custom validation
|
|
are reported or raised as all other cssutils exceptions depending
|
|
on cssutils.log.raiseExceptions which e.g during parsing normally
|
|
is False so the exceptions would be logged only.
|
|
:param macros:
|
|
may be used in the given properties definitions. There are some
|
|
predefined basic macros which may always be used in
|
|
:attr:`Profiles._TOKEN_MACROS` and :attr:`Profiles._MACROS`.
|
|
"""
|
|
if not macros:
|
|
macros = {}
|
|
m = Profiles._TOKEN_MACROS.copy()
|
|
m.update(Profiles._MACROS)
|
|
m.update(macros)
|
|
properties = self._expand_macros(properties, m)
|
|
self._profileNames.append(profile)
|
|
self._profiles[profile] = self._compile_regexes(properties)
|
|
|
|
self.__update_knownNames()
|
|
|
|
def removeProfile(self, profile=None, all=False):
|
|
"""Remove `profile` or remove `all` profiles.
|
|
|
|
:param profile:
|
|
profile name to remove
|
|
:param all:
|
|
if ``True`` removes all profiles to start with a clean state
|
|
:exceptions:
|
|
- :exc:`cssutils.profiles.NoSuchProfileException`:
|
|
If given `profile` cannot be found.
|
|
"""
|
|
if all:
|
|
self._profiles.clear()
|
|
del self._profileNames[:]
|
|
else:
|
|
try:
|
|
del self._profiles[profile]
|
|
del self._profileNames[self._profileNames.index(profile)]
|
|
except KeyError:
|
|
raise NoSuchProfileException(u'No profile %r.' % profile)
|
|
|
|
self.__update_knownNames()
|
|
|
|
def propertiesByProfile(self, profiles=None):
|
|
"""Generator: Yield property names, if no `profiles` is given all
|
|
profile's properties are used.
|
|
|
|
:param profiles:
|
|
a single profile name or a list of names.
|
|
"""
|
|
if not profiles:
|
|
profiles = self.profiles
|
|
elif isinstance(profiles, basestring):
|
|
profiles = (profiles, )
|
|
try:
|
|
for profile in sorted(profiles):
|
|
for name in sorted(self._profiles[profile].keys()):
|
|
yield name
|
|
except KeyError, e:
|
|
raise NoSuchProfileException(e)
|
|
|
|
def validate(self, name, value):
|
|
"""Check if `value` is valid for given property `name` using **any**
|
|
profile.
|
|
|
|
:param name:
|
|
a property name
|
|
:param value:
|
|
a CSS value (string)
|
|
:returns:
|
|
if the `value` is valid for the given property `name` in any
|
|
profile
|
|
"""
|
|
for profile in self.profiles:
|
|
if name in self._profiles[profile]:
|
|
try:
|
|
# custom validation errors are caught
|
|
r = bool(self._profiles[profile][name](value))
|
|
except Exception, e:
|
|
self._log.error(e, error=Exception)
|
|
return False
|
|
if r:
|
|
return r
|
|
return False
|
|
|
|
def validateWithProfile(self, name, value, profiles=None):
|
|
"""Check if `value` is valid for given property `name` returning
|
|
``(valid, profile)``.
|
|
|
|
:param name:
|
|
a property name
|
|
:param value:
|
|
a CSS value (string)
|
|
:param profiles:
|
|
internal parameter used by Property.validate only
|
|
:returns:
|
|
``valid, matching, profiles`` where ``valid`` is if the `value`
|
|
is valid for the given property `name` in any profile,
|
|
``matching==True`` if it is valid in the given `profiles`
|
|
and ``profiles`` the profile names for which the value is valid
|
|
(or ``[]`` if not valid at all)
|
|
|
|
Example::
|
|
|
|
>>> cssutils.profile.defaultProfiles = cssutils.profile.CSS_LEVEL_2
|
|
>>> print cssutils.profile.validateWithProfile('color', 'rgba(1,1,1,1)')
|
|
(True, False, Profiles.CSS3_COLOR)
|
|
"""
|
|
if name not in self.knownNames:
|
|
return False, False, []
|
|
else:
|
|
if not profiles:
|
|
profiles = self.defaultProfiles
|
|
elif isinstance(profiles, basestring):
|
|
profiles = (profiles, )
|
|
|
|
for profilename in profiles:
|
|
# check given profiles
|
|
if name in self._profiles[profilename]:
|
|
validate = self._profiles[profilename][name]
|
|
try:
|
|
if validate(value):
|
|
return True, True, [profilename]
|
|
except Exception, e:
|
|
self._log.error(e, error=Exception)
|
|
|
|
for profilename in (p for p in self._profileNames
|
|
if p not in profiles):
|
|
# check remaining profiles as well
|
|
if name in self._profiles[profilename]:
|
|
validate = self._profiles[profilename][name]
|
|
try:
|
|
if validate(value):
|
|
return True, False, [profilename]
|
|
except Exception, e:
|
|
self._log.error(e, error=Exception)
|
|
|
|
names = []
|
|
for profilename, properties in self._profiles.items():
|
|
# return profile to which name belongs
|
|
if name in properties.keys():
|
|
names.append(profilename)
|
|
names.sort()
|
|
return False, False, names
|
|
|
|
|
|
properties = {}
|
|
macros = {}
|
|
"""
|
|
Define some regular expression fragments that will be used as
|
|
macros within the CSS property value regular expressions.
|
|
"""
|
|
macros[Profiles.CSS_LEVEL_2] = {
|
|
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
|
|
'border-color': '{color}',
|
|
'border-width': '{length}|thin|medium|thick',
|
|
|
|
'background-color': r'{color}|transparent|inherit',
|
|
'background-image': r'{uri}|none|inherit',
|
|
#'background-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?)|inherit',
|
|
'background-position': r'({percentage}|{length}|left|center|right)(\s*({percentage}|{length}|top|center|bottom))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?)|inherit',
|
|
'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
|
|
'background-attachment': r'scroll|fixed|inherit',
|
|
|
|
'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
|
|
'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
|
|
'identifier': r'{ident}',
|
|
'family-name': r'{string}|{identifier}({w}{identifier})*',
|
|
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
|
|
'absolute-size': r'(x?x-)?(small|large)|medium',
|
|
'relative-size': r'smaller|larger',
|
|
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
|
|
'font-size': r'{absolute-size}|{relative-size}|{positivelength}|{percentage}|inherit',
|
|
'font-style': r'normal|italic|oblique|inherit',
|
|
'font-variant': r'normal|small-caps|inherit',
|
|
'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
|
|
'line-height': r'normal|{number}|{length}|{percentage}|inherit',
|
|
'list-style-image': r'{uri}|none|inherit',
|
|
'list-style-position': r'inside|outside|inherit',
|
|
'list-style-type': r'disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-(latin|alpha)|upper-(latin|alpha)|armenian|georgian|none|inherit',
|
|
'margin-width': r'{length}|{percentage}|auto',
|
|
'outline-color': r'{color}|invert|inherit',
|
|
'outline-style': r'{border-style}|inherit',
|
|
'outline-width': r'{border-width}|inherit',
|
|
'padding-width': r'{length}|{percentage}',
|
|
'specific-voice': r'{identifier}',
|
|
'generic-voice': r'male|female|child',
|
|
'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
|
|
'border-attrs': r'{border-width}|{border-style}|{border-color}',
|
|
'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
|
|
'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
|
|
'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
|
|
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
|
|
'text-attrs': r'underline|overline|line-through|blink',
|
|
'overflow': r'visible|hidden|scroll|auto|inherit',
|
|
}
|
|
|
|
"""
|
|
Define the regular expressions for validation all CSS values
|
|
"""
|
|
properties[Profiles.CSS_LEVEL_2] = {
|
|
'azimuth': r'{angle}|(behind\s+)?(left-side|far-left|left|center-left|center|center-right|right|far-right|right-side)(\s+behind)?|behind|leftwards|rightwards|inherit',
|
|
'background-attachment': r'{background-attachment}',
|
|
'background-color': r'{background-color}',
|
|
'background-image': r'{background-image}',
|
|
'background-position': r'{background-position}',
|
|
'background-repeat': r'{background-repeat}',
|
|
# Each piece should only be allowed one time
|
|
'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
|
|
'border-collapse': r'collapse|separate|inherit',
|
|
'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
|
|
'border-spacing': r'{length}(\s+{length})?|inherit',
|
|
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
|
|
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
|
'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
|
'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
|
'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
|
'border-top-color': r'{border-color}|transparent|inherit',
|
|
'border-right-color': r'{border-color}|transparent|inherit',
|
|
'border-bottom-color': r'{border-color}|transparent|inherit',
|
|
'border-left-color': r'{border-color}|transparent|inherit',
|
|
'border-top-style': r'{border-style}|inherit',
|
|
'border-right-style': r'{border-style}|inherit',
|
|
'border-bottom-style': r'{border-style}|inherit',
|
|
'border-left-style': r'{border-style}|inherit',
|
|
'border-top-width': r'{border-width}|inherit',
|
|
'border-right-width': r'{border-width}|inherit',
|
|
'border-bottom-width': r'{border-width}|inherit',
|
|
'border-left-width': r'{border-width}|inherit',
|
|
'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
|
|
'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
|
'bottom': r'{length}|{percentage}|auto|inherit',
|
|
'caption-side': r'top|bottom|inherit',
|
|
'clear': r'none|left|right|both|inherit',
|
|
'clip': r'{shape}|auto|inherit',
|
|
'color': r'{color}|inherit',
|
|
'content': r'none|normal|{content}(\s+{content})*|inherit',
|
|
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
|
'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
|
'cue-after': r'{uri}|none|inherit',
|
|
'cue-before': r'{uri}|none|inherit',
|
|
'cue': r'({uri}|none|inherit){1,2}|inherit',
|
|
'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
|
|
'direction': r'ltr|rtl|inherit',
|
|
'display': r'inline|block|list-item|run-in|inline-block|table|inline-table|table-row-group|table-header-group|table-footer-group|table-row|table-column-group|table-column|table-cell|table-caption|none|inherit',
|
|
'elevation': r'{angle}|below|level|above|higher|lower|inherit',
|
|
'empty-cells': r'show|hide|inherit',
|
|
'float': r'left|right|none|inherit',
|
|
'font-family': r'{font-family}',
|
|
'font-size': r'{font-size}',
|
|
'font-style': r'{font-style}',
|
|
'font-variant': r'{font-variant}',
|
|
'font-weight': r'{font-weight}',
|
|
'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
|
|
'height': r'{length}|{percentage}|auto|inherit',
|
|
'left': r'{length}|{percentage}|auto|inherit',
|
|
'letter-spacing': r'normal|{length}|inherit',
|
|
'line-height': r'{line-height}',
|
|
'list-style-image': r'{list-style-image}',
|
|
'list-style-position': r'{list-style-position}',
|
|
'list-style-type': r'{list-style-type}',
|
|
'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
|
|
'margin-right': r'{margin-width}|inherit',
|
|
'margin-left': r'{margin-width}|inherit',
|
|
'margin-top': r'{margin-width}|inherit',
|
|
'margin-bottom': r'{margin-width}|inherit',
|
|
'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
|
|
'max-height': r'{length}|{percentage}|none|inherit',
|
|
'max-width': r'{length}|{percentage}|none|inherit',
|
|
'min-height': r'{length}|{percentage}|none|inherit',
|
|
'min-width': r'{length}|{percentage}|none|inherit',
|
|
'orphans': r'{integer}|inherit',
|
|
'outline-color': r'{outline-color}',
|
|
'outline-style': r'{outline-style}',
|
|
'outline-width': r'{outline-width}',
|
|
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
|
|
'overflow': r'{overflow}',
|
|
'padding-top': r'{padding-width}|inherit',
|
|
'padding-right': r'{padding-width}|inherit',
|
|
'padding-bottom': r'{padding-width}|inherit',
|
|
'padding-left': r'{padding-width}|inherit',
|
|
'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
|
|
'page-break-after': r'auto|always|avoid|left|right|inherit',
|
|
'page-break-before': r'auto|always|avoid|left|right|inherit',
|
|
'page-break-inside': r'avoid|auto|inherit',
|
|
'pause-after': r'{time}|{percentage}|inherit',
|
|
'pause-before': r'{time}|{percentage}|inherit',
|
|
'pause': r'({time}|{percentage}){1,2}|inherit',
|
|
'pitch-range': r'{number}|inherit',
|
|
'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
|
|
'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
|
|
'position': r'static|relative|absolute|fixed|inherit',
|
|
'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
|
|
'richness': r'{number}|inherit',
|
|
'right': r'{length}|{percentage}|auto|inherit',
|
|
'speak-header': r'once|always|inherit',
|
|
'speak-numeral': r'digits|continuous|inherit',
|
|
'speak-punctuation': r'code|none|inherit',
|
|
'speak': r'normal|none|spell-out|inherit',
|
|
'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
|
|
'stress': r'{number}|inherit',
|
|
'table-layout': r'auto|fixed|inherit',
|
|
'text-align': r'left|right|center|justify|inherit',
|
|
'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
|
|
'text-indent': r'{length}|{percentage}|inherit',
|
|
'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
|
|
'top': r'{length}|{percentage}|auto|inherit',
|
|
'unicode-bidi': r'normal|embed|bidi-override|inherit',
|
|
'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
|
|
'visibility': r'visible|hidden|collapse|inherit',
|
|
'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
|
|
'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
|
|
'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
|
|
'widows': r'{integer}|inherit',
|
|
'width': r'{length}|{percentage}|auto|inherit',
|
|
'word-spacing': r'normal|{length}|inherit',
|
|
'z-index': r'auto|{integer}|inherit',
|
|
}
|
|
|
|
# CSS Box Module Level 3
|
|
macros[Profiles.CSS3_BOX] = {
|
|
'overflow': macros[Profiles.CSS_LEVEL_2]['overflow']
|
|
}
|
|
properties[Profiles.CSS3_BOX] = {
|
|
'overflow': '{overflow}{w}{overflow}?|inherit',
|
|
'overflow-x': '{overflow}|inherit',
|
|
'overflow-y': '{overflow}|inherit'
|
|
}
|
|
|
|
# CSS Color Module Level 3
|
|
macros[Profiles.CSS3_COLOR] = {
|
|
# orange and transparent in CSS 2.1
|
|
'namedcolor': r'(currentcolor|transparent|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)',
|
|
# orange?
|
|
'rgbacolor': r'rgba\({w}{int}{w},{w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgba\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w},{w}{num}{w}\)',
|
|
'hslcolor': r'hsl\({w}{int}{w},{w}{num}%{w},{w}{num}%{w}\)|hsla\({w}{int}{w},{w}{num}%{w},{w}{num}%{w},{w}{num}{w}\)',
|
|
'x11color': r'aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen',
|
|
'uicolor': r'(ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)',
|
|
}
|
|
properties[Profiles.CSS3_COLOR] = {
|
|
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{rgbacolor}|{hslcolor}|inherit',
|
|
'opacity': r'{num}|inherit'
|
|
}
|
|
|
|
# CSS Fonts Module Level 3 http://www.w3.org/TR/css3-fonts/
|
|
macros[Profiles.CSS3_FONTS] = {
|
|
'family-name': r'{string}|{ident}', # but STRING is effectively an IDENT???
|
|
'font-face-name': 'local\({w}{ident}{w}\)',
|
|
'font-stretch-names': r'(ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded)',
|
|
'unicode-range': r'[uU]\+[0-9A-Fa-f?]{1,6}(\-[0-9A-Fa-f]{1,6})?'
|
|
}
|
|
properties[Profiles.CSS3_FONTS] = {
|
|
'font-size-adjust': r'{number}|none|inherit',
|
|
'font-stretch': r'normal|wider|narrower|{font-stretch-names}|inherit'
|
|
}
|
|
properties[Profiles.CSS3_FONT_FACE] = {
|
|
'font-family': '{family-name}',
|
|
'font-stretch': r'{font-stretch-names}',
|
|
'font-style': r'normal|italic|oblique',
|
|
'font-weight': r'normal|bold|[1-9]00',
|
|
'src': r'({uri}{w}(format\({w}{string}{w}(\,{w}{string}{w})*\))?|{font-face-name})({w},{w}({uri}{w}(format\({w}{string}{w}(\,{w}{string}{w})*\))?|{font-face-name}))*',
|
|
'unicode-range': '{unicode-range}({w},{w}{unicode-range})*'
|
|
}
|
|
|
|
|
|
|
|
# CSS3 Paged Media
|
|
macros[Profiles.CSS3_PAGED_MEDIA] = {
|
|
'pagesize': 'a5|a4|a3|b5|b4|letter|legal|ledger',
|
|
'pagebreak': 'auto|always|avoid|left|right'
|
|
}
|
|
properties[Profiles.CSS3_PAGED_MEDIA] = {
|
|
'fit': 'fill|hidden|meet|slice',
|
|
'fit-position': r'auto|(({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?))',
|
|
'image-orientation': 'auto|{angle}',
|
|
'orphans': r'{integer}|inherit',
|
|
'page': 'auto|{ident}',
|
|
'page-break-before': '{pagebreak}|inherit',
|
|
'page-break-after': '{pagebreak}|inherit',
|
|
'page-break-inside': 'auto|avoid|inherit',
|
|
'size': '({length}{w}){1,2}|auto|{pagesize}{w}(?:portrait|landscape)',
|
|
'widows': r'{integer}|inherit'
|
|
}
|
|
|
|
|