def normalize_edge(name, cssvalue):
    style = {}
    if isinstance(cssvalue, PropertyValue):
        primitives = [css_text(v) for v in cssvalue]
    else:
        primitives = [css_text(cssvalue)]
    if len(primitives) == 1:
        value, = primitives
        values = (value, value, value, value)
    elif len(primitives) == 2:
        vert, horiz = primitives
        values = (vert, horiz, vert, horiz)
    elif len(primitives) == 3:
        top, horiz, bottom = primitives
        values = (top, horiz, bottom, horiz)
    else:
        values = primitives[:4]
    if '-' in name:
        l, _, r = name.partition('-')
        for edge, value in zip(EDGES, values):
            style['%s-%s-%s' % (l, edge, r)] = value
    else:
        for edge, value in zip(EDGES, values):
            style['%s-%s' % (name, edge)] = value
    return style
示例#2
0
def remove_links_to(container, predicate):
    ''' predicate must be a function that takes the arguments (name, href,
    fragment=None) and returns True iff the link should be removed '''
    from ebook_converter.ebooks.oeb.base import iterlinks, OEB_DOCS, OEB_STYLES, XPath, XHTML
    stylepath = XPath('//h:style')
    styleattrpath = XPath('//*[@style]')
    changed = set()
    for name, mt in container.mime_map.items():
        removed = False
        if mt in OEB_DOCS:
            root = container.parsed(name)
            for el, attr, href, pos in iterlinks(root,
                                                 find_links_in_css=False):
                hname = container.href_to_name(href, name)
                frag = href.partition('#')[-1]
                if predicate(hname, href, frag):
                    if attr is None:
                        el.text = None
                    else:
                        if el.tag == XHTML('link') or el.tag == XHTML('img'):
                            extract(el)
                        else:
                            del el.attrib[attr]
                    removed = True
            for tag in stylepath(root):
                if tag.text and (tag.get('type')
                                 or 'text/css').lower() == 'text/css':
                    sheet = container.parse_css(tag.text)
                    if remove_links_in_sheet(
                            partial(container.href_to_name, base=name), sheet,
                            predicate):
                        tag.text = css_text(sheet)
                        removed = True
            for tag in styleattrpath(root):
                style = tag.get('style')
                if style:
                    style = container.parse_css(style, is_declaration=True)
                    if remove_links_in_declaration(
                            partial(container.href_to_name, base=name), style,
                            predicate):
                        removed = True
                        tag.set('style', css_text(style))
        elif mt in OEB_STYLES:
            removed = remove_links_in_sheet(
                partial(container.href_to_name, base=name),
                container.parsed(name), predicate)
        if removed:
            changed.add(name)
    tuple(map(container.dirty, changed))
    return changed
示例#3
0
def remove_property_value(prop, predicate):
    ''' Remove the Values that match the predicate from this property. If all
    values of the property would be removed, the property is removed from its
    parent instead. Note that this means the property must have a parent (a
    CSSStyleDeclaration). '''
    removed_vals = list(filter(predicate, prop.propertyValue))
    if len(removed_vals) == len(prop.propertyValue):
        prop.parent.removeProperty(prop.name)
    else:
        x = base.css_text(prop.propertyValue)
        for v in removed_vals:
            x = x.replace(base.css_text(v), '').strip()
        prop.propertyValue.cssText = x
    return bool(removed_vals)
 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(css_text(style), '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'))
def normalize_simple_composition(name,
                                 cssvalue,
                                 composition,
                                 check_inherit=True):
    if check_inherit and css_text(cssvalue) == 'inherit':
        style = {k: 'inherit' for k in composition}
    else:
        style = {k: DEFAULTS[k] for k in composition}
        try:
            primitives = [css_text(v) for v in cssvalue]
        except TypeError:
            primitives = [css_text(cssvalue)]
        while primitives:
            value = primitives.pop()
            for key in composition:
                if cssprofiles.validate(key, value):
                    style[key] = value
                    break
    return style
示例#6
0
 def __call__(self, oeb):
     if not self.body_font_family:
         return None
     if not self.href:
         iid, href = oeb.manifest.generate('page_styles', 'page_styles.css')
         rules = [base.css_text(x) for x in self.rules]
         rules = '\n\n'.join(rules)
         sheet = css_parser.parseString(rules, validate=False)
         self.href = oeb.manifest.add(iid,
                                      href,
                                      mimetypes.guess_type(href)[0],
                                      data=sheet).href
     return self.href
示例#7
0
def get_font_properties(rule, default=None):
    '''
    Given a CSS rule, extract normalized font properties from
    it. Note that shorthand font property should already have been expanded
    by the CSS flattening code.
    '''
    props = {}
    s = rule.style
    for q in ('font-family', 'src', 'font-weight', 'font-stretch',
            'font-style'):
        g = 'uri' if q == 'src' else 'value'
        try:
            val = s.getProperty(q).propertyValue[0]
            val = getattr(val, g)
            if q == 'font-family':
                val = parse_font_family(css_text(s.getProperty(q).propertyValue))
                if val and val[0] == 'inherit':
                    val = None
        except (IndexError, KeyError, AttributeError, TypeError, ValueError):
            val = None if q in {'src', 'font-family'} else default
        if q in {'font-weight', 'font-stretch', 'font-style'}:
            val = str(val).lower() if (val or val == 0) else val
            if val == 'inherit':
                val = default
        if q == 'font-weight':
            val = {'normal':'400', 'bold':'700'}.get(val, val)
            if val not in {'100', '200', '300', '400', '500', '600', '700',
                    '800', '900', 'bolder', 'lighter'}:
                val = default
            if val == 'normal':
                val = '400'
        elif q == 'font-style':
            if val not in {'normal', 'italic', 'oblique'}:
                val = default
        elif q == 'font-stretch':
            if val not in {'normal', 'ultra-condensed', 'extra-condensed',
                    'condensed', 'semi-condensed', 'semi-expanded',
                    'expanded', 'extra-expanded', 'ultra-expanded'}:
                val = default
        props[q] = val
    return props
示例#8
0
    def collect_global_css(self):
        global_css = collections.defaultdict(list)
        for item in self.items:
            stylizer = self.stylizers[item]
            if float(self.context.margin_top) >= 0:
                stylizer.page_rule['margin-top'] = '%gpt'%\
                        float(self.context.margin_top)
            if float(self.context.margin_bottom) >= 0:
                stylizer.page_rule['margin-bottom'] = '%gpt'%\
                        float(self.context.margin_bottom)
            items = sorted(stylizer.page_rule.items())
            css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
            css = ('@page {\n%s\n}\n' % css) if items else ''
            rules = [
                base.css_text(r)
                for r in stylizer.font_face_rules + self.embed_font_rules
            ]
            raw = '\n\n'.join(rules)
            css += '\n\n' + raw
            global_css[css].append(item)

        gc_map = {}
        manifest = self.oeb.manifest
        for css in global_css:
            href = None
            if css.strip():
                id_, href = manifest.generate('page_css', 'page_styles.css')
                sheet = css_parser.parseString(css, validate=False)
                if self.transform_css_rules:
                    from ebook_converter.ebooks.css_transform_rules import transform_sheet
                    transform_sheet(self.transform_css_rules, sheet)
                manifest.add(id_, href, base.CSS_MIME, data=sheet)
            gc_map[css] = href

        ans = {}
        for css, items in global_css.items():
            for item in items:
                ans[item] = gc_map[css]
        return ans
def normalize_font(cssvalue, font_family_as_list=False):
    # See https://developer.mozilla.org/en-US/docs/Web/CSS/font
    composition = font_composition
    val = css_text(cssvalue)
    if val == 'inherit':
        ans = {k: 'inherit' for k in composition}
    elif val in {
            'caption', 'icon', 'menu', 'message-box', 'small-caption',
            'status-bar'
    }:
        ans = {k: DEFAULTS[k] for k in composition}
    else:
        ans = {k: DEFAULTS[k] for k in composition}
        ans.update(parse_font(val))
    if font_family_as_list:
        if isinstance(ans['font-family'], (str, bytes)):
            ans['font-family'] = [
                x.strip() for x in ans['font-family'].split(',')
            ]
    else:
        if not isinstance(ans['font-family'], (str, bytes)):
            ans['font-family'] = serialize_font_family(ans['font-family'])
    return ans