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_args(s, dimensions=1): if s.endswith("||"): s = s[:-2] s = re.sub(r"\t", r"\\t", s) double_bar_pat = re.compile(r"\|\|") bar_pat = re.compile(r"\s*\|\s*") parts = [e.strip() for e in double_bar_pat.split(s.strip())] for p in parts: elts = bar_pat.split(p.strip()) elts = ["~" if (e.strip() == "") else e.strip() for e in elts] result = [bar_pat.split(e) for e in parts] if dimensions == 1: result = result[0] return result def parse_length(target, s, rel_fraction): def choose(html_value, tex_value): return html_value if target == "html" else tex_value pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:"([^"]+)")') #pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:{([^}]+)})') #pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(f)') match = pat.match(s) if match is None: # But already checked by the klammer raise Exception(f'The argument "{s}" is not a length') #print(match.groups()) num, units, fit, text = match.groups() num = float(num) if num else "" num *= rel_fraction if fit: result = "f" # choose("100vw", "\\textwidth") elif text: text = re.compile(r"\{\}\\textbackslash\{\}").sub(r"\\", text) if text[0] == "-": result = choose("", f"\\textwidth - \\widthof{{ {text[1:]}}}") else: result = choose("", f"\\widthof{{ {text}}}") elif units == "w": result = choose(f"{100 * num}vw", f"{num}\\textwidth") elif units == "h": result = choose(f"{100 * num}vh", f"{num}\\textheight") elif units == "pw": result = choose(f"{100 * num}vw", f"{num}\\paperwidth") elif units == "ph": result = choose(f"{100 * num}vh", f"{num}\\paperheight") elif units == "px": result = choose(f"{round(num)}", f"{num}px") else: result = f"{num}{units}" result = re.sub("\t", "\\t", result) return result, num, units def old_parse_length(target, s): pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|("([^"]+)")') match = pat.match(s) if match is None: raise Exception(f'The argument "{s}" is not a length') if match.group(3) in {"none", "f"}: return "f" if s[0] == '"': if target in {"tex", "pdf"}: return f"\\widthof{{{match.group(5)} }}" else: return "" num, units = match.groups()[:2] if float(num) == 0: return None result = f"{num}{units}" if target in {"tex", "pdf"}: if units == "w": result = fr"{num}\textwidth" elif units == "h": result = fr"{numb}\textheight" elif target in {"html"}: scale_x = None scale_y = None if units == "w": #result = fr"{int(float(num)*100)}vw" result = fr"calc({int(float(num)*100)}vw - 2rem)" #result = 0 scale_x = num result = "none" elif units == "h": result = fr"{int(float(num)*100)}vw" #result = 0 scale_y = num result = "none" result = [result, scale_x, scale_y] else: raise Exception(f"Uknown length: '{s}'") return result def parse_lengths(target, s): # Fill strings with space character for the split: #print("parse_lengths:", s) def replace(match): return re.sub(r"\s", "~", match.group(1)) result = re.compile(r'("[^"]+")').sub(replace, s) #print(result, result.split()) result = [parse_length(target, e)[0] for e in result.split()] return result paragraph_separator_re = re.compile(r'\n *\n', re.S) def format_for_paragraphs(s): result = s if len(paragraph_separator_re.findall(result)) > 0: result = '\n\n{}\n\n'.format(result) return result