def csscombine( path=None, url=None, cssText=None, href=None, sourceencoding=None, targetencoding=None, minify=True, resolveVariables=True, ): """Combine sheets referred to by @import rules in given CSS proxy sheet into a single new sheet. :returns: combined cssText, normal or minified :Parameters: `path` or `url` or `cssText` + `href` path or URL to a CSSStyleSheet or a cssText of a sheet which imports other sheets which are then combined into one sheet. `cssText` normally needs `href` to be able to resolve relative imports. `sourceencoding` = 'utf-8' explicit encoding of the source proxysheet `targetencoding` encoding of the combined stylesheet `minify` = True defines if the combined sheet should be minified, in this case comments are not parsed at all! `resolveVariables` = True defines if variables in combined sheet should be resolved """ cssutils.log.info('Combining files from %r' % url, neverraise=True) if sourceencoding is not None: cssutils.log.info('Using source encoding %r' % sourceencoding, neverraise=True) parser = cssutils.CSSParser(parseComments=not minify) if path and not cssText: src = parser.parseFile(path, encoding=sourceencoding) elif url: src = parser.parseUrl(url, encoding=sourceencoding) elif cssText: src = parser.parseString(cssText, href=href, encoding=sourceencoding) else: sys.exit('Path or URL must be given') result = cssutils.resolveImports(src) result.encoding = targetencoding cssutils.log.info('Using target encoding: %r' % targetencoding, neverraise=True) oldser = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) if minify: cssutils.ser.prefs.useMinified() cssutils.ser.prefs.resolveVariables = resolveVariables cssText = result.cssText cssutils.setSerializer(oldser) return cssText
def csscombine( path=None, url=None, cssText=None, href=None, sourceencoding=None, targetencoding=None, minify=True, resolveVariables=True, ): """Combine sheets referred to by @import rules in given CSS proxy sheet into a single new sheet. :returns: combined cssText, normal or minified :Parameters: `path` or `url` or `cssText` + `href` path or URL to a CSSStyleSheet or a cssText of a sheet which imports other sheets which are then combined into one sheet. `cssText` normally needs `href` to be able to resolve relative imports. `sourceencoding` = 'utf-8' explicit encoding of the source proxysheet `targetencoding` encoding of the combined stylesheet `minify` = True defines if the combined sheet should be minified, in this case comments are not parsed at all! `resolveVariables` = True defines if variables in combined sheet should be resolved """ cssutils.log.info(u"Combining files from %r" % url, neverraise=True) if sourceencoding is not None: cssutils.log.info(u"Using source encoding %r" % sourceencoding, neverraise=True) parser = cssutils.CSSParser(parseComments=not minify) if path and not cssText: src = parser.parseFile(path, encoding=sourceencoding) elif url: src = parser.parseUrl(url, encoding=sourceencoding) elif cssText: src = parser.parseString(cssText, href=href, encoding=sourceencoding) else: sys.exit("Path or URL must be given") result = cssutils.resolveImports(src) result.encoding = targetencoding cssutils.log.info(u"Using target encoding: %r" % targetencoding, neverraise=True) oldser = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) if minify: cssutils.ser.prefs.useMinified() cssutils.ser.prefs.resolveVariables = resolveVariables cssText = result.cssText cssutils.setSerializer(oldser) return cssText
def minify_sources(sources, ext, fs_root='', timestamp=False): """Use utilities to minify javascript or css. :param sources: Paths of source files :param ext: Type of files :param fs_root: root of file (normally public dir) :type sources: string :type ext: js or css :type fs_root: string :returns: List of paths to minified sources """ if 'js' in ext: js_minify = JavascriptMinify() minified_sources = [] for source in sources: # generate full path to source no_ext_source = path.splitext(source)[0] full_source = path.join(fs_root, (no_ext_source + ext).lstrip('/')) # generate minified source path full_source = path.join(fs_root, (source).lstrip('/')) no_ext_full_source = path.splitext(full_source)[0] minified = no_ext_full_source + ext f_minified_source = open(minified, 'w') try: # minify js source (read stream is auto-closed inside) if 'js' in ext: js_minify.minify(open(full_source, 'r'), f_minified_source) # minify css source if 'css' in ext: sheet = cssutils.parseFile(full_source) cssutils.setSerializer(CSSUtilsMinificationSerializer()) cssutils.ser.prefs.useMinified() f_minified_source.write(sheet.cssText) finally: f_minified_source.close() if no_ext_source.endswith('COMBINED'): minified_sources.append(no_ext_source + ext) else: minified_sources.append(no_ext_source + generate_timestamp(timestamp) + ext) return minified_sources
def test_setCSSSerializer(self): "cssutils.setSerializer() and cssutils.ser" s = cssutils.parseString('a { left: 0 }') exp4 = '''a { left: 0 }''' exp1 = '''a { left: 0 }''' self.assertEqual(exp4, s.cssText) newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' ')) cssutils.setSerializer(newser) self.assertEqual(exp1, s.cssText) newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' ')) cssutils.ser = newser self.assertEqual(exp4, s.cssText)
def test_setCSSSerializer(self): "cssutils.setSerializer() and cssutils.ser" s = cssutils.parseString('a { left: 0 }') exp4 = '''a { left: 0 }''' exp1 = '''a { left: 0 }''' self.assertEqual(exp4.encode(), s.cssText) newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' ')) cssutils.setSerializer(newser) self.assertEqual(exp1.encode(), s.cssText) newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' ')) cssutils.ser = newser self.assertEqual(exp4.encode(), s.cssText)
def csscombine(path=None, url=None, sourceencoding=None, targetencoding=None, minify=True): """Combine sheets referred to by @import rules in given CSS proxy sheet into a single new sheet. :returns: combined cssText, normal or minified :Parameters: `path` or `url` path or URL to a CSSStyleSheet which imports other sheets which are then combined into one sheet `targetencoding` encoding of the combined stylesheet, default 'utf-8' `minify` defines if the combined sheet should be minified, default True """ cssutils.log.info(u'Combining files from %r' % url, neverraise=True) if sourceencoding is not None: cssutils.log.info(u'Using source encoding %r' % sourceencoding, neverraise=True) if path: src = cssutils.parseFile(path, encoding=sourceencoding) elif url: src = cssutils.parseUrl(url, encoding=sourceencoding) else: sys.exit('Path or URL must be given') result = cssutils.resolveImports(src) result.encoding = targetencoding cssutils.log.info(u'Using target encoding: %r' % targetencoding, neverraise=True) if minify: # save old setting and use own serializer oldser = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) cssutils.ser.prefs.useMinified() cssText = result.cssText cssutils.setSerializer(oldser) else: cssText = result.cssText return cssText
def filter_file_content(self, file_content, path): """URLs are made relative to the target and the CSS is minified.""" if self.rewrite_urls: src_dir = os.path.dirname(path) relative_src_dir = relative_path(self.target_file, src_dir) if relative_src_dir == ".": relative_parts = [] else: relative_parts = relative_src_dir.split(os.path.sep) def fix_relative_url(match): url = match.group(1) # Don't modify absolute URLs or 'data:' urls. if (url.startswith("http") or url.startswith("/") or url.startswith("data:")): return match.group(0) parts = relative_parts + url.split("/") result = [] for part in parts: if part == ".." and result and result[-1] != "..": result.pop(-1) continue result.append(part) return "url(%s)" % "/".join( filter(None, [self.resource_prefix] + result)) file_content = URL_RE.sub(fix_relative_url, file_content) if self.minify: old_serializer = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) try: cssutils.ser.prefs.useMinified() stylesheet = ESCAPE_STAR_PROPERTY_RE.sub( r'\1_ie_star_hack:', file_content) settings.set('DXImageTransform.Microsoft', True) parser = cssutils.CSSParser(raiseExceptions=True) css = parser.parseString(stylesheet) stylesheet = UNESCAPE_STAR_PROPERTY_RE.sub( r'*\1:', css.cssText) return stylesheet + "\n" finally: cssutils.setSerializer(old_serializer) return file_content + "\n"
def filter_file_content(self, file_content, path): """URLs are made relative to the target and the CSS is minified.""" if self.rewrite_urls: src_dir = os.path.dirname(path) relative_parts = relative_path(self.target_file, src_dir).split( os.path.sep) def fix_relative_url(match): url = match.group(1) # Don't modify absolute URLs or 'data:' urls. if (url.startswith("http") or url.startswith("/") or url.startswith("data:")): return match.group(0) parts = relative_parts + url.split("/") result = [] for part in parts: if part == ".." and result and result[-1] != "..": result.pop(-1) continue result.append(part) return "url(%s)" % "/".join( filter(None, [self.resource_prefix] + result)) file_content = URL_RE.sub(fix_relative_url, file_content) if self.minify: old_serializer = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) try: cssutils.ser.prefs.useMinified() stylesheet = ESCAPE_STAR_PROPERTY_RE.sub( r'\1_ie_star_hack:', file_content) settings.set('DXImageTransform.Microsoft', True) parser = cssutils.CSSParser(raiseExceptions=True) css = parser.parseString(stylesheet) stylesheet = UNESCAPE_STAR_PROPERTY_RE.sub( r'*\1:', css.cssText) return stylesheet + "\n" finally: cssutils.setSerializer(old_serializer) return file_content + "\n"
def csscombine(path=None, url=None, sourceencoding=None, targetencoding=None, minify=True): """Combine sheets referred to by @import rules in given CSS proxy sheet into a single new sheet. :returns: combined cssText, normal or minified :Parameters: `path` or `url` path or URL to a CSSStyleSheet which imports other sheets which are then combined into one sheet `targetencoding` encoding of the combined stylesheet, default 'utf-8' `minify` defines if the combined sheet should be minified, default True """ cssutils.log.info(u"Combining files from %r" % url, neverraise=True) if sourceencoding is not None: cssutils.log.info(u"Using source encoding %r" % sourceencoding, neverraise=True) if path: src = cssutils.parseFile(path, encoding=sourceencoding) elif url: src = cssutils.parseUrl(url, encoding=sourceencoding) else: sys.exit("Path or URL must be given") result = cssutils.resolveImports(src) result.encoding = targetencoding cssutils.log.info(u"Using target encoding: %r" % targetencoding, neverraise=True) if minify: # save old setting and use own serializer oldser = cssutils.ser cssutils.setSerializer(cssutils.serialize.CSSSerializer()) cssutils.ser.prefs.useMinified() cssText = result.cssText cssutils.setSerializer(oldser) else: cssText = result.cssText return cssText
def setUp(self): cssutils.setSerializer(customcssutils.MyCSSSerializer()) self.css = cssutils.parseFile(os.path.join(os.path.dirname(__file__), 'resources', 'base.css'))
def run(bk): # set custom serializer if Sigil version is < 0.9.18 (0.9.18 and higher have the new css-parser module) if bk.launcher_version() < 20190826: cssutils.setSerializer(customcssutils.MyCSSSerializer()) prefs = get_prefs(bk) xml_parser = etree.XMLParser(resolve_entities=False) css_parser = cssutils.CSSParser(raiseExceptions=True, validate=False) css_to_skip, css_to_parse, css_warnings = pre_parse_css(bk, css_parser) form = InfoDialog(bk, prefs) form.parse_errors(bk, css_to_skip, css_to_parse, css_warnings) form.mainloop() if InfoDialog.stop_plugin: return -1 parseAllXMLFiles = prefs['parseAllXMLFiles'] # Parse files to create the list of "orphaned selectors" orphaned_selectors = [] for css_id, css_href in bk.css_iter(): if css_id not in css_to_skip.keys(): css_string = read_css(bk, css_id) parsed_css = css_parser.parseString(css_string) namespaces_dict, default_prefix = css_namespaces(parsed_css) for rule in style_rules(parsed_css): for selector_index, selector in enumerate(rule.selectorList): maintain_selector = False if ignore_selectors(selector.selectorText): continue # If css specifies a default namespace, the default prefix # must be added to every unprefixed type selector. if default_prefix: selector_ns = add_default_prefix( default_prefix, selector.selectorText) else: selector_ns = selector.selectorText selector_ns = clean_generic_prefixes(selector_ns) for file_id, href, mime in bk.manifest_iter(): if mime == 'application/xhtml+xml': is_xhtml = True elif parseAllXMLFiles and re.search( r'[/+]xml\b', mime): is_xhtml = False else: continue if selector_exists( etree.HTML( bk.readfile(file_id).encode('utf-8')), selector_ns, namespaces_dict, is_xhtml): maintain_selector = True break try: if selector_exists( etree.XML( bk.readfile(file_id).encode('utf-8'), xml_parser), selector_ns, namespaces_dict, is_xhtml): maintain_selector = True break except etree.XMLSyntaxError: form = ErrorDlg(href_to_basename(href)) form.mainloop() return 1 if not maintain_selector: orphaned_selectors.append( (css_id, rule, rule.selectorList[selector_index], selector_index, parsed_css)) # Show the list of selectors to the user. form = SelectorsDialog(bk, orphaned_selectors) form.mainloop() if SelectorsDialog.stop_plugin: return -1 # Delete selectors chosen by the user. css_to_change = {} old_rule, counter = None, 0 for sel_data, to_delete in SelectorsDialog.orphaned_dict.values(): if to_delete.get() == 1: if sel_data[1] == old_rule: counter += 1 else: counter = 0 del sel_data[1].selectorList[sel_data[3] - counter] old_rule = sel_data[1] css_to_change[sel_data[0]] = sel_data[4].cssText for css_id, css_text in css_to_change.items(): bk.writefile(css_id, css_text) return 0
def run(bk): cssutils.setSerializer(customcssutils.MyCSSSerializer()) prefs = get_prefs(bk) xml_parser = etree.XMLParser(resolve_entities=False) css_parser = cssutils.CSSParser(raiseExceptions=True, validate=False) css_to_skip, css_to_parse, css_warnings = pre_parse_css(bk, css_parser) form = InfoDialog(bk, prefs) form.parse_errors(bk, css_to_skip, css_to_parse, css_warnings) form.mainloop() if InfoDialog.stop_plugin: return -1 parseAllXMLFiles = prefs['parseAllXMLFiles'] # Parse files to create the list of "orphaned selectors" orphaned_selectors = [] for css_id, css_href in bk.css_iter(): if css_id not in css_to_skip.keys(): css_string = read_css(bk, css_id) parsed_css = css_parser.parseString(css_string) namespaces_dict, default_prefix = css_namespaces(parsed_css) for rule in style_rules(parsed_css): for selector_index, selector in enumerate(rule.selectorList): maintain_selector = False if ignore_selectors(selector.selectorText): continue # If css specifies a default namespace, the default prefix # must be added to every unprefixed type selector. if default_prefix: selector_ns = add_default_prefix(default_prefix, selector.selectorText) else: selector_ns = selector.selectorText selector_ns = clean_generic_prefixes(selector_ns) for file_id, href, mime in bk.manifest_iter(): if mime == 'application/xhtml+xml': is_xhtml = True elif parseAllXMLFiles and re.search(r'[/+]xml\b', mime): is_xhtml = False else: continue if selector_exists(etree.HTML(bk.readfile(file_id).encode('utf-8')), selector_ns, namespaces_dict, is_xhtml): maintain_selector = True break try: if selector_exists(etree.XML(bk.readfile(file_id).encode('utf-8'), xml_parser), selector_ns, namespaces_dict, is_xhtml): maintain_selector = True break except etree.XMLSyntaxError: form = ErrorDlg(href_to_basename(href)) form.mainloop() return 1 if not maintain_selector: orphaned_selectors.append((css_id, rule, rule.selectorList[selector_index], selector_index, parsed_css)) # Show the list of selectors to the user. form = SelectorsDialog(bk, orphaned_selectors) form.mainloop() if SelectorsDialog.stop_plugin: return -1 # Delete selectors chosen by the user. css_to_change = {} old_rule, counter = None, 0 for sel_data, to_delete in SelectorsDialog.orphaned_dict.values(): if to_delete.get() == 1: if sel_data[1] == old_rule: counter += 1 else: counter = 0 del sel_data[1].selectorList[sel_data[3]-counter] old_rule = sel_data[1] css_to_change[sel_data[0]] = sel_data[4].cssText for css_id, css_text in css_to_change.items(): bk.writefile(css_id, css_text) return 0