import sys import os import re __all__ = ["Formatter"] try: version = os.environ['VERSION_NUMBER'] except: version = "" styles = { 'b': "\\fB@\\fR", 'i': "\\fI@\\fR", 'em': "\\fI@\\fR", 'code': "\\fC@\\fR", 'span': "\\fC@\\fR", 'sup': "\\u@\\d", 'hr': "" } formats = { 'br': "\n.br\n", 'h2': "\n.SH @", 'h3': "\n.SS @", 'h4': "\n.SS @", 'dt': ("\n.IP \"@\" 4m", 'no_nl'), 'dd': "\n.br\n@", 'ul': ("\n.RS 4n\n@\n.RE\n", 'in_ul'), 'menu': ("\n.RS 4n\n@\n.RE\n", 'in_ul'), 'dir': ("\n.RS 4n\n@\n.RE\n", 'in_ul'), 'ol': ("\n.IP\n@\n.PP\n", 'index'), 'p': "\n.PP\n@", 'pre': ("\n.br\n.nf\n\\fC\n@\n\\fR\n.fi\n", 'preformat') } formats.update(styles) def is_string(x): return isinstance(x, str) def is_tuple(x): return isinstance(x, tuple) def is_list(x): return isinstance(x, list) def is_blank(s): return is_string(s) and s.strip() == "" def clean(content): return [item for item in content if not is_blank(item)] class Formatter: def __init__(self, filename, stream = sys.stdout): self.stream = stream self.style = dict(preformat = False, in_ul = False, no_nl = False, in_table = False, in_tr = False, index = []) self.stack = [] self.strip_re = re.compile("^[ \t]+") self.filename = filename self.at_bol = True def warning(self, msg): sys.stderr.write(msg + '\n') def set(self, var, val): self.style[var] = val def get(self, var): return self.style[var] def push(self, **kwargs): self.stack.append(self.style.copy()) self.style.update(**kwargs) def pop(self): self.style = self.stack.pop() def show(self, s): self.stream.write(s) if s != '': self.at_bol = s.endswith('\n') def pp_with(self, content, var, val): self.push() self.set(var, val) self.pp(content) self.pop() def fmt(self, format, content, var = None): # String.partition is only in 2.5+ # (pre,sep,post) = format.partition("@") if self.get('no_nl') and '\n' in format: self.warning("can't handle line breaks in
...
") format = "@" f = format.split('@', 1) pre = f[0] if len(f) > 1: sep = '@' post = f[1] else: sep = '' post = '' if pre != "": self.show(pre) if sep != "": if var: if var == 'index': val = self.get('index') + [0] else: val = True self.pp_with(content, var, val) else: self.pp(content) if post != "": self.show(post) def pp_li(self, content): if self.get('in_ul'): self.fmt("\n.IP \(bu 4n\n@", content) else: idx = self.get('index') idx[-1] += 1 sec = ".".join(map(str,idx)) self.show("\n.IP \\fB%s\\fR\n" % sec) self.set('index', idx) self.pp(content) def pp_title(self): self.show("\n.TH " + os.path.basename(self.filename).replace(".html","") + " 1 \"\" \"GRASS " + version + "\" \"Grass User's Manual\"") def pp_tr(self, content): content = clean(content) self.push(in_tr = True) col = 0 for item in content: if not is_tuple(item): self.warning("invalid item in table row: %s" % str(item)) continue (tag, attrs, body) = item if tag not in ['td', 'th']: self.warning("invalid tag in table row: %s" % tag) continue if col > 0: self.show("\t \t") self.show("T{\n") self.pp(body) self.show("\nT}") col += 1 self.show("\n") self.pop() def pp_tbody(self, content): for item in content: if is_tuple(item): (tag, attrs, body) = item if tag in ['thead', 'tbody', 'tfoot']: self.pp_tbody(body) elif tag == 'tr': self.pp_tr(body) self.show(".sp 1\n") def count_cols(self, content): cols = 0 for item in content: n = 0 if is_blank(item): pass elif is_tuple(item): (tag, attrs, body) = item if tag in ['thead', 'tbody', 'tfoot']: n = self.count_cols(body) elif tag == 'tr': n = len(clean(body)) cols = max(cols, n) else: self.warning("invalid item in table: %s" % str(item)) return cols def pp_table(self, content): cols = self.count_cols(content) if cols == 0: return self.show("\n.TS\nexpand;\n") self.show(" lw1 ".join(["lw60" for i in range(cols)]) + ".\n") self.pp_tbody(content) self.show("\n.TE\n") def pp_tag(self, tag, content): if self.get('in_tr') and tag not in styles: self.pp(content) elif tag in formats: spec = formats[tag] if is_string(spec): self.fmt(spec, content) else: (fmt, var) = spec self.fmt(fmt, content, var) elif tag == 'table': if self.get('in_table'): self.warning("cannot handle nested tables") return self.push(in_table = True) self.pp_table(content) self.pop() elif tag == 'li': self.pp_li(content) elif tag == 'title': self.pp_title() else: self.pp(content) def pp_string(self, content): if content == "": return s = content if self.get('no_nl'): s = s.replace("\n"," ") s = s.replace("\\", "\\(rs") s = s.replace("'", "\\(cq") s = s.replace("\"", "\\(dq") s = s.replace("`", "\\(ga") s = s.replace("-", "\\-") if self.at_bol and s[0] in [".","'"]: s = "\\&" + s self.show(s) def pp_text(self, content): if content == "": return lines = content.splitlines(True) if len(lines) != 1: for line in lines: self.pp_text(line) return else: content = lines[0] if self.at_bol and not self.get('preformat'): content = self.strip_re.sub('', content) self.pp_string(content) def pp_list(self, content): for item in content: self.pp(item) def pp(self, content): if is_list(content): self.pp_list(content) elif is_tuple(content): (tag, attrs, body) = content self.pp_tag(tag, body) elif is_string(content): self.pp_text(content)