#! /usr/bin/python3 # Functions that generate HTML for reports and such. LastEdit = "Last edited on 2025-07-27 11:17:15 by stolfi" import sys, re, subprocess def out(txt): sys.stdout.write(txt) # ---------------------------------------------------------------------- def preamble(wr, title, max_width): wr.write("\n") wr.write("\n") wr.write("\n") wr.write("\n") wr.write(f"{title}\n") wr.write("\n") wr.write(f"\n") wr.write("
\n") wr.write("\n") # Initial state: # 'cur_level': nesting level of the next things to be written. st = dict(cur_level = 0) return st # ---------------------------------------------------------------------- def section(wr, st, level, title): st['cur_level'] = level wr.write(indent(st, f"{title}\n")) return # ---------------------------------------------------------------------- def parag(wr, st, txt): txt = fix_markup(txt) # Prefix the indentation blanks: txt = indent(st, "

" + txt + "

\n") wr.write(txt) return # ---------------------------------------------------------------------- def fmt_item(st, txt): # Formats a list item. DOES NOT PRINT, returns it instead. st['cur_level'] += 1 txt = fix_markup(txt) txt = indent(st, "
  • " + txt + "
  • ") st['cur_level'] -= 1 return txt # ---------------------------------------------------------------------- def ulist(wr, st, items): # The {items} must be a list of items already formatted, including # proper indentation. wr.write(indent(st, "\n")) return # ---------------------------------------------------------------------- def indent(st, txt): # Prepends the proper number of spaces to {txt}, # according to {st['cur_level']} level = st['cur_level'] assert level >= 1, f"bad level {level}" txt = (" " * (level - 1)) + txt return txt # ---------------------------------------------------------------------- def figure(wr, st, fig_width, image, caption): caption = f"

    " + fix_markup(caption) + "

    " ind = " " * st['cur_level'] wr.write(ind); wr.write( " \n") wr.write(ind); wr.write( " \n") wr.write(ind); wr.write(f" \n") wr.write(ind); wr.write( " \n") wr.write(ind); wr.write( " \n") wr.write(ind); wr.write(f" \n") wr.write(ind); wr.write( " \n") wr.write(ind); wr.write( "
    {image}
    {caption}
    \n") return # ---------------------------------------------------------------------- def postamble(wr, st): global LastEdit wr.write("
    \n") wr.write("

    \n") wr.write(f"{LastEdit}\n") wr.write("

    \n") wr.write("\n") wr.write("\n") wr.write("\n") sys.stdout.flush() return # ---------------------------------------------------------------------- def fix_markup(txt): # Join lines into a single line: txt = re.sub(r"[ ]*[\n][ ]*", " ", txt) # Remover leading and trailing blanks: txt = txt.strip() txt = re.sub(r"([ .,()<>])[/]([^/]+)[/]([ .,()<>])", r"\1\2\3", txt) txt = re.sub(r"([ .,()<>])[*]([^*]+)[*]([ .,()<>])", r"\1\2\3", txt) txt = re.sub(r'["]', r""", txt) return txt # ---------------------------------------------------------------------- def get_text_from_file(fname): rd = open(fname, 'r') lines = rd.readlines() txt = ' '.join(lines) txt = fix_markup(txt) rd.close() return txt # ---------------------------------------------------------------------- def get_image_size(fname): cmd = [ "convert", fname, "-print", "'%[fx:w] %[fx:h]'", "null:-" ] res = str(subprocess.check_output(cmd).strip()) size = re.split(r"[ ]+", res) assert len(size) == 2, f"bad command output '{res}'" size = ( int(x) for x in size ) return size # ----------------------------------------------------------------------