import os import re import inspect import html_util from html_util import E def black_text(): return "\033[0;30m" def blue_text(text): return f"\033[0;34m{text}\033[0m" def red_text(text): return f"\033[31m{text}\033[0m" def msg(text=""): frame = inspect.currentframe().f_back print(blue_text(f"[{os.path.basename(frame.f_code.co_filename)}:{frame.f_lineno}]"), text) def escape(s): result = s #result = re.sub(r"\b", r"\b", result) #result = re.sub(r"\t", r"\t", result) #result = re.sub("\f", r"\\f", result) #result = re.sub("\v", r"\\v", result) #result = re.compile("\\(.)").sub(r"\\\1", result) return result def klammertext_dir(): var = 'KLAMMERTEXT_HOME' kdir = os.environ.get(var) if kdir is None: raise Exception( f"The environment variable {var} must be defined as the top-level Klammertext directory") return kdir def cache_directory(relative_pathname, basename): directory = relative_pathname if not os.path.isdir(directory): os.makedirs(directory) result = directory + "/_klammertext_cache/" + basename return result; def sks_dirs(): result = 'kutil book document block font link section list image table code color'.split() k = klammertext_dir() result = [[e, f"{k}/sks/{e}"] for e in result] return result def sks_files_of_type(extension): result = [] for base, sks_dir in sks_dirs(): filename = f'{sks_dir}/{base}.{extension}' if os.path.exists(filename): result.append([base, filename]) return result def make_dir_if_necessary(d, delete_contents=False): if not os.path.exists(d): os.makedirs(d) if delete_contents: os.system(f'rm -rf {d}/*') def protect_klammertext_special_characters(text): result = text result = re.compile(r"\^").sub("H̅", result) result = re.compile(r"@").sub("A̅", result) result = re.compile(r":").sub("̅C̅", result) result = re.compile(r"\|").sub("̅B̅", result) return result # When did this ever make sense? Before the new word patterns, probably. def bar_delimiter(): return "\3" def double_bar_delimiter(): return "\4" def caption_delimiter(): return "__CAPTION__" def caption_marker(name, caption, delimiter=" - "): caption = re.sub("\n", " ", caption) d = caption_delimiter() if caption: caption = f"{delimiter}{caption}" return f"{d}{name}{d}{caption}{d}" def rest_split(s, dimensions=1): """Split bar-delimited text into nested lists, one level per dimension. The delimiter for dimension n is a run of exactly n bar characters: | separates elements, || separates lists of elements, ||| lists of lists, and so on. This is the cast behind the rest(N) argument type. One trailing top-level delimiter (the customary dangling separator before a closing @) is removed; all other empty elements are preserved, so a trailing | still makes an empty final cell. """ s = s.strip() if not s: return [] if dimensions > 0 else s delimiter = "|" * dimensions if s.endswith(delimiter) and not s.endswith("|" + delimiter): s = s[:-len(delimiter)] return _rest_split_level(s, dimensions) def _rest_split_level(s, dimensions): if dimensions <= 0: return s.strip() pattern = re.compile("(? 0: result = '\n\n{}\n\n'.format(result) return result