"""functions to add css to html""" import os import re import sys import lxml.etree as etree import filing from nitpo import Nitpo from sheets import Sheets from xpaths import Xpaths import docing class Inces(Nitpo): def __init__(self, do_verbose=False): super().__init__() self.sheets = Sheets() self.do_verbose = do_verbose self.css_doc = self.load_css() # # parse for variable substitution self.x = Xpaths() self.var = None self.prefix = None self.doc = None def trans_fufi(self, fufi, locs=None): """external callers transform a fufi""" if not os.path.isfile(fufi): if self.do_verbose: print(f"inces does not see {fufi}", file=sys.stderr) return None if not fufi.endswith('.xhtml'): print("inces needs .xhtml input", file=sys.stderr) doc = filing.parse_lax(fufi) # # if we run from the comand line, do don’t have the locs locs = self.read_locs() out_doc = self.trans_doc(doc, locs) out_fufi = fufi[:-6] + '.html' string = docing.show(out_doc) has_it_been_written = filing.srite(out_fufi, string, do_verbose=self.do_verbose) return has_it_been_written def trans_doc(self, doc, locs): """external callers by doc""" self.prefix = '{' + self.get_ns(doc) + '}' self.remove_css_link(doc) eles = self.x.run(doc, '//*') for ele in eles: self.trans_ele(ele, locs) return doc def remove_css_link(self, doc): xp = "/h:html/h:head/h:link[@rel='stylesheet']" link_ele = self.x.none_or_one(doc, xp) if link_ele is None: return False link_ele.getparent().remove(link_ele) return True def get_ns(self, doc): """there should be a better way to do this""" root_ele = doc.getroot() tag = root_ele.tag tag = tag[1:] ns = tag.partition('}')[0] self.ns_len = len(ns) self.const['nsmap']['h'] = ns ## makes is available to self.x self.x.const['nsmap'] = self.const['nsmap'] return ns def trans_ele(self, ele, locs): if 'class' in ele.attrib: the_class = '.' + ele.attrib['class'] if the_class in locs['class']: self.add_style(ele, locs['class'][the_class]) if 'id' in ele.attrib: the_id = '#' + ele.attrib['id'] if the_id in locs['id']: self.add_style(ele, locs['id'][the_id]) ## the should be a better way to get the local name name = ele.tag.replace(self.prefix, '') if name in locs['name']: self.add_style(ele, locs['name'][name]) def add_style(self, ele, string): if 'style' in ele.attrib: ele.attrib['style'] += ' ' + string return ele.attrib['style'] = string def get_res(self): res = {} ## simple class res['class'] = re.compile(r'\.[a-z_]+$') res['id'] = re.compile(r'#[a-z_]+$') res['name'] = re.compile(r'[a-z]([a-z]+|[0-9])$') return res def get_var(self): xp = '/n:rules/n:var' var_eles = self.x.run(self.css_doc, xp) var = {} for var_ele in var_eles: if 'name' not in var_ele.attrib: err = docing.show(var_ele) print("inces: no name for variable {err}", file=sys.stderr) continue if 'value' not in var_ele.attrib: err = docing.show(var_ele) print("inces: no value for variable {err}", file=sys.stderr) continue var[var_ele.attrib['name']] = var_ele.attrib['value'] return var def read_locs(self): """externel caller gets the locs""" # # populate self.css_doc self.css_doc = self.load_css() # # variable names set in css.xml (var= vs lit=) self.var = self.get_var() # # regular expressions to parse element self.res = self.get_res() # # we don't use /n:rules/n:media, should be obsoleted anyway xp = '/n:rules/n:rule/n:loc' loc_eles = self.x.run(self.css_doc, xp) locs = {} # # make sure these always exist locs['id'] = {} locs['class'] = {} locs['name'] = {} for loc_ele in loc_eles: loc = loc_ele.text found = False for name in self.res: if self.res[name].match(loc): found = True rules = self.get_rules(loc) locs[name][loc] = rules break if not found and self.do_verbose: print(f"inces can not implement '{loc}'") continue return locs def load_css(self): if not self.has_conf('files', 'css_xml'): print("inces needs Ф[files][css_xml]") quit() css_fufi = self.conf['files']['css_xml'] css_doc = filing.parse_lax(css_fufi) return css_doc def get_rules(self, loc): xp = f"/n:rules/n:rule/n:loc[text()='{loc}']" rule_eles = self.x.run(self.css_doc, xp) if len(rule_eles) == 0: if self.do_verbose: print(f"inces can not find a rule for {loc}") return None xp = '../n:prop' rules = {} for rule_ele in rule_eles: prop_eles = self.x.run(rule_ele, xp) for prop_ele in prop_eles: if 'name' not in prop_ele.attrib: err = docing.show(prop_ele) print("inces: no name for variable {err}", file=sys.stderr) continue name = prop_ele.attrib['name'] if name in rules: if self.do_verbose: print(f"inces find duplicate rule {name} in {loc}") else: rules[name] = '' # # normal case if 'lit' in prop_ele.attrib: rules[name] += prop_ele.attrib['lit'] continue # # no @lit, no @var if 'var' not in prop_ele.attrib: if self.do_verbose: print("inces: no lit= and no var= in {name} at {loc}") continue # # var case var = prop_ele.attrib['var'] if var not in self.var: if self.do_verbose: print("inces sees refer error for {name} in {loc}") continue rules[name] += self.var[var] out = self.format_rules(rules) return out def format_rules(self, rules): out = '' for name in rules: out += name + ': ' + rules[name] + '; ' out = out[:-2] return out