Source code for unitparser

# python 2
from __future__ import absolute_import

# builtins
from string import digits

# custom
import blowdrycss_settings as settings

__author__ = 'chad nelson'
__project__ = 'blowdrycss'


[docs]class UnitParser(object): """ **Used in these cases:** - No units are provided and default units need to be added to make it valid css. - The user wants their pixel (px) based units to be converted to em or root em (rem) so that their page scales / zooms properly. **Assumption:** The value provided already has negative signs and decimal points. There are no dashes or underscores present in the value e.g. -1.25 can be processed, but n1_25 cannot be processed. **Contains a ``default_property_units_dict``** which maps property names to their default units. **Note:** Shorthand properties are not supported. **Why do I want to use em (named after the sound for the letter 'M') or root em (rem)?:** *Because your webpage will scale with browser and device size.* | .. http://snook.ca/archives/html_and_css/font-size-with-rem https://css-tricks.com/rems-ems/ **What does (em) actually stand for?:** **Source:** W3C -- http://www.w3.org/WAI/GL/css2em.htm The foremost tool for writing scalable style sheets is the "em" unit, and it therefore goes on top of the list of guidelines that we will compile throughout this chapter: use ems to make scalable style sheets. Named after the letter "M", the em unit has a long-standing tradition in typography where it has been used to measure horizontal widths. ... In CSS, the em unit is a general unit for measuring lengths, for example page margins and padding around elements. You can use it both horizontally and vertically, and this shocks traditional typographers who always have used em exclusively for horizontal measurements. By extending the em unit to also work vertically, it has become a very powerful unit - so powerful that you seldom have to use other length units. **Source:** Wikipedia -- https://en.wikipedia.org/wiki/Em_%28typography%29 An em is a unit in the field of typography, equal to the currently specified point size. For example, one em in a 16-point typeface is 16 points. Therefore, this unit is the same for all typefaces at a given point size. """ def __init__(self, property_name=''): self.property_name = property_name self.allowed = set(digits + '-.px') # Reference: http://www.w3.org/TR/CSS21/propidx.html # Extracted all properties containing Values of <angle>, <percentage>, <length>, <time>, <frequency> # IDEA: Build webscraper that auto-extracts these. May not be deterministic enough. Would need to build a # Page based on the standard that includes all property name/value combos. self.default_property_units_dict = { # Number of possible values: 'background-position': '%', # single or double # 'border': 'px', # single Shorthand Property unit addition Not implemented 'border-top': 'px', # single 'border-right': 'px', # single 'border-bottom': 'px', # single 'border-left': 'px', # single 'border-spacing': 'px', # single 'border-width': 'px', # single 'border-top-width': 'px', # single 'border-right-width': 'px', # single 'border-bottom-width': 'px', # single 'border-left-width': 'px', # single 'border-radius': 'px', # single 'border-top-left-radius': 'px', # single 'border-top-right-radius': 'px', # single 'border-bottom-right-radius': 'px', # single 'border-bottom-left-radius': 'px', 'elevation': 'deg', # single # 'font': 'px', # single Shorthand Property unit addition Not implemented 'font-size': 'px', # single 'height': 'px', # single 'max-height': 'px', # single 'min-height': 'px', # single 'letter-spacing': 'px', # single 'word-spacing': 'px', # single 'line-height': 'px', # single 'top': 'px', # single 'right': 'px', # single 'bottom': 'px', # single 'left': 'px', # single 'margin': 'px', # single, double, quadruple 'margin-top': 'px', # single 'margin-right': 'px', # single 'margin-bottom': 'px', # single 'margin-left': 'px', # single # 'outline': 'px', # single Shorthand Property unit addition Not implemented 'outline-width': 'px', # single 'padding': 'px', # single, double, quadruple 'padding-top': 'px', # single 'padding-right': 'px', # single 'padding-bottom': 'px', # single 'padding-left': 'px', # single 'pause': 'ms', # single, double 'pause-after': 'ms', # single 'pause-before': 'ms', # single 'pitch': 'Hz', # single 'text-indent': 'px', # single 'text-shadow': 'px', # single, double, triple 'vertical-align': '%', # single 'volume': '%', # single 'width': 'px', # single 'max-width': 'px', # single 'min-width': 'px', # single }
[docs] def default_units(self): """ Returns the default units "if any" for the assigned ``self.property_name``. :return: (*str*) -- Returns default units for the assigned ``self.property_name`` if they exist. Otherwise, return an empty string ``''``. """ if self.property_name in self.default_property_units_dict: return self.default_property_units_dict[self.property_name] else: return ''
[docs] def add_units(self, property_value=''): """ If the property_name requires units, then apply the default units defined in default_property_units_dict. **Rules:** - If use_em is False apply the default units for the property name by looking it up in default_property_units_dict. - Unit that have default units of ``px`` are converted to ``em`` if use_em is True. - If ``property_value`` has multiple property values, then split it apart. - If the value already has units, then pass it through unchanged. - The value provided shall possess negative signs and decimal points. - Mixed units are allowed, but **not recommended**. - Values shall only contain [] e.g. -1.25 can be processed, but n1_25 cannot be processed. :type property_value: str :param property_value: A string containing one or more space delimited alphanumeric characters. :return: (str) -- Returns the property value with the default or converted units added. >>> # Convert 'px' to 'em' >>> unit_parser = UnitParser(property_name='padding', use_em=True) >>> unit_parser.add_units('1 2 1 2') 0.0625em 0.125em 0.0625em 0.125em >>> # Use default units >>> unit_parser.use_em = False >>> unit_parser.add_units('1 2 1 2') 1px 2px 1px 2px >>> # Values already have units or are not parsable pass through >>> # True produces the same output. >>> unit_parser.use_em = False >>> unit_parser.add_units('55zp') 55zp >>> unit_parser.add_units('17rem') 17rem >>> # Unitless ``property_name`` >>> # causes ``property_value`` to pass through. >>> unit_parser.property_name = 'font-weight' >>> unit_parser.add_units('200') 200 >>> # Mixed units cases - Not a Recommended Practice, >>> # but represent valid CSS. Be careful. >>> unit_parser.use_em = False >>> unit_parser.add_units('5em 6 5em 6') 5em 6px 5em 6px >>> unit_parser.use_em = True >>> unit_parser.add_units('1em 100 4cm 9rem') 1em 6.25em 4cm 9rem """ new_value = [] try: default_units = self.default_property_units_dict[self.property_name] # See if property_name has units. for val in property_value.split(): # single, double and quadruple if set(val) <= self.allowed: val = val.replace('px', '') # Handle 'px' units case. if settings.use_em and default_units == 'px': # Convert units if required. new_value.append(settings.px_to_em(pixels=val)) else: new_value.append(val + default_units) # Use default units. else: new_value.append(val) # Pass through and ignore value. property_value = ' '.join(new_value) # Put the new values back together. except KeyError: pass # Property is unitless. return property_value