Initial commit: Klammertext source distribution
Curated source subset assembled by klammertext-dev's doc/make_dist.sh: the Klammermachine (mac), the Standard Klammer Set (sks), the commands (com), editor plugins and install guides (doc), a test subset (tst), and lib/bin placeholders. Builds with 'make -C com'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
209
sks/kutil/kutil.py
Normal file
209
sks/kutil/kutil.py
Normal file
@@ -0,0 +1,209 @@
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user