2026-07-18 18:48:23 +02:00
|
|
|
import pprint
|
|
|
|
|
import re
|
|
|
|
|
import textwrap
|
|
|
|
|
import kutil
|
|
|
|
|
import klammer_base
|
2026-09-16 16:59:49 +02:00
|
|
|
import phases
|
2026-07-18 18:48:23 +02:00
|
|
|
import itertools
|
|
|
|
|
import math
|
|
|
|
|
from html_util import E
|
|
|
|
|
|
|
|
|
|
class List(klammer_base.Klammer_base):
|
|
|
|
|
def __init__(self, K, list_type):
|
|
|
|
|
super().__init__(K)
|
|
|
|
|
self.list_type = list_type
|
2026-07-22 18:17:43 +02:00
|
|
|
self.items = self.list_items
|
2026-07-18 18:48:23 +02:00
|
|
|
word_count = max([len(e.strip().split()) for e in self.items])
|
|
|
|
|
self.compressed = False #and word_count < int(self.List_compressed_word_max)
|
|
|
|
|
|
|
|
|
|
def html(self):
|
|
|
|
|
body = ""
|
|
|
|
|
word_count = 0
|
|
|
|
|
for e in self.items:
|
|
|
|
|
body += f"<li>\n{kutil.format_for_paragraphs(e.strip())}\n</li>\n"
|
|
|
|
|
cls = "compressed" if self.compressed else ""
|
|
|
|
|
result = E(self.list_type).cls(cls).body(body.strip()).str().strip() + "\n"
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def tex(self):
|
|
|
|
|
body = "\n\n".join([f"\\item {e}" for e in self.items])
|
|
|
|
|
command = {'ol' : 'enumerate', 'ul' : 'itemize'}[self.list_type]
|
|
|
|
|
init = ""
|
2026-09-18 20:02:44 +02:00
|
|
|
# :initial is @ol's alone (list.k); a @ul has no such attribute.
|
|
|
|
|
if getattr(self, "initial", 1) != 1:
|
2026-07-22 18:17:43 +02:00
|
|
|
init = "\\addtocounter{enumi}{" + str(self.initial - 1) + "}"
|
2026-07-18 18:48:23 +02:00
|
|
|
#topsep = '[noitemsep,topsep=0pt]'
|
|
|
|
|
topsep = "[noitemsep,topsep=0pt," if self.compressed else "[topsep=0pt,itemsep=2pt,"
|
|
|
|
|
topsep += "leftmargin=16pt]"
|
2026-07-22 18:17:43 +02:00
|
|
|
listsep = '\\setlist{nolistsep}' if self.cmp else ''
|
2026-07-18 18:48:23 +02:00
|
|
|
result = f'{listsep}\n\\begin{{{command}}}{topsep}{init}\n{body}\n\\end{{{command}}}\n'
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def txt(self):
|
|
|
|
|
n = 1
|
|
|
|
|
result = ""
|
|
|
|
|
for item in self.items:
|
|
|
|
|
prefix = f"{n:3}. " if self.list_type == "ol" else " • " # U+2022
|
|
|
|
|
print("prefix", prefix)
|
|
|
|
|
formatted_item = "\n".join(
|
|
|
|
|
textwrap.wrap(item.strip(), width=80, subsequent_indent=" "*(len(prefix))))
|
|
|
|
|
result += f"{prefix}{formatted_item}\n"
|
|
|
|
|
if not self.compressed:
|
|
|
|
|
result += "\n"
|
|
|
|
|
n += 1
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Define(klammer_base.Klammer_base):
|
|
|
|
|
def __init__(self, K):
|
|
|
|
|
super().__init__(K)
|
2026-07-22 18:17:43 +02:00
|
|
|
self.items = self.descriptions
|
2026-07-18 18:48:23 +02:00
|
|
|
#self.show()
|
|
|
|
|
#pprint.pprint(self.items)
|
|
|
|
|
|
|
|
|
|
def html(self):
|
|
|
|
|
def fontify(s):
|
|
|
|
|
return {"r" : s,
|
|
|
|
|
"i" : f"<i>{s}</i>",
|
|
|
|
|
"b" : f"<b>{s}</b>",
|
|
|
|
|
"t" : f'<span class="monospace">{s}</span>',
|
|
|
|
|
"c" : f'<span class="monospace">{s}</span>',
|
2026-08-25 23:30:55 +02:00
|
|
|
"s" : f'<span class="sansserif">{s}</span>'}[self.style]
|
2026-07-18 18:48:23 +02:00
|
|
|
result = "\n\n".join([f"<dt>{fontify(term)}</dt>\n<dd>{definition}</dd>"
|
|
|
|
|
for term,definition in self.items])
|
|
|
|
|
result = f"<dl>\n{result}\n</dl>"
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def tex(self):
|
|
|
|
|
def fontify(s):
|
|
|
|
|
font = {'r' : r'\normalfont',
|
|
|
|
|
'i' : r'\normalfont\itshape',
|
|
|
|
|
'b' : r'\normalfont\bfseries',
|
|
|
|
|
't' : r'\normalfont\ttfamily',
|
|
|
|
|
'c' : r'\normalfont\ttfamily',
|
2026-08-25 23:30:55 +02:00
|
|
|
's' : r'\normalfont\sffamily'}[self.style]
|
2026-07-18 18:48:23 +02:00
|
|
|
return f"{font} {s}"
|
2026-09-16 16:59:49 +02:00
|
|
|
strut = r"\rule[-4pt]{0pt}{4pt}"
|
2026-08-18 00:28:09 +02:00
|
|
|
result = "\n\n".join([f"\\item[{{{fontify(term)}{strut}}}] {definition}"
|
2026-07-18 18:48:23 +02:00
|
|
|
for term,definition in self.items])
|
2026-09-16 16:59:49 +02:00
|
|
|
options = "style=nextline" # See list/list.sty
|
|
|
|
|
result = f"\\begin{{description}}[{options}]\n{result}\n\\end{{description}}"
|
2026-07-18 18:48:23 +02:00
|
|
|
return result
|
|
|
|
|
|
2026-09-16 16:59:49 +02:00
|
|
|
def txt(self):
|
|
|
|
|
# Plain text: each term on its own line at the block indent, its
|
|
|
|
|
# definition beneath it indented once more, items separated by a
|
|
|
|
|
# blank line. Every line begins with the nbsp marker, which is what
|
|
|
|
|
# tells the justification pass the block is already laid out
|
|
|
|
|
# (phases.txt_justify_blocks); a definition's own paragraphs are
|
|
|
|
|
# wrapped to the remaining measure, and a paragraph that is already
|
|
|
|
|
# laid out (it begins with the marker -- a @table inside the
|
|
|
|
|
# definition) is only indented further. The term takes :style as
|
|
|
|
|
# italic or bold, the styles a terminal can show.
|
|
|
|
|
nbsp = phases.txt_nbsp_marker()
|
|
|
|
|
indent = int(self.Target_txt_indent)
|
|
|
|
|
term_indent, def_indent = nbsp * indent, nbsp * (2 * indent)
|
|
|
|
|
measure = int(self.Target_txt_width) - 2 * indent
|
|
|
|
|
style = {"i": "italic", "b": "bold"}.get(self.style)
|
|
|
|
|
items = []
|
|
|
|
|
for term, definition in self.items:
|
|
|
|
|
term = term.strip()
|
|
|
|
|
if style and term:
|
|
|
|
|
term = phases.txt_style_marker(style) + term + phases.txt_style_marker(style, False)
|
|
|
|
|
paragraphs = []
|
|
|
|
|
for par in re.split(r"\n\s*\n", definition.strip()):
|
|
|
|
|
if phases.txt_is_laid_out(par):
|
|
|
|
|
paragraphs.append("\n".join(def_indent + line for line in par.split("\n")))
|
|
|
|
|
else:
|
|
|
|
|
paragraphs.append("\n".join(def_indent + line
|
|
|
|
|
for line in phases.txt_wrap(par, measure)))
|
|
|
|
|
items.append(term_indent + term + "\n" + "\n\n".join(paragraphs))
|
|
|
|
|
return phases.txt_lay_out("\n\n".join(items))
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Lines(klammer_base.Klammer_base):
|
|
|
|
|
def __init__(self, K):
|
|
|
|
|
super().__init__(K)
|
|
|
|
|
|
|
|
|
|
def html(self):
|
2026-08-23 20:48:26 +02:00
|
|
|
#return " <br/>".join(self.s.strip().split("\n"))
|
2026-07-18 18:48:23 +02:00
|
|
|
#return f'<pre style="font-family: var(--serif)">{self.s}</pre>'
|
|
|
|
|
#return f'<pre>{self.s}</pre>'
|
|
|
|
|
result = self.s
|
2026-08-23 20:48:26 +02:00
|
|
|
result = re.sub("\n", "<br/>\n", result)
|
2026-07-18 18:48:23 +02:00
|
|
|
result = re.sub(" ", "&^#160;", result)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def tex(self):
|
|
|
|
|
return r"\\\newline\n".join(self.s.strip().split("\n"))
|
|
|
|
|
|
|
|
|
|
def txt(self):
|
|
|
|
|
return self.s.strip()
|
|
|
|
|
|
|
|
|
|
|
2026-09-16 16:59:49 +02:00
|
|
|
class Columns(klammer_base.Klammer_base):
|
|
|
|
|
def __init__(self, K):
|
|
|
|
|
super().__init__(K)
|
|
|
|
|
|
|
|
|
|
def make_column_lists(self):
|
|
|
|
|
lst = [f"@{self.style} {e} {self.style}@" for e in self.items.split()]
|
|
|
|
|
cols = math.ceil(len(lst) / self.n)
|
|
|
|
|
result = []
|
|
|
|
|
for i in range(self.n):
|
|
|
|
|
result.append(lst[i * cols : (i+1) * cols])
|
|
|
|
|
return list(itertools.zip_longest(*result, fillvalue=""))
|
|
|
|
|
|
|
|
|
|
def columns(self):
|
|
|
|
|
rows = self.make_column_lists()
|
|
|
|
|
result = (f"@table :header false :number false :leading 1.0 "
|
|
|
|
|
f":hpos {self.hpos} :offset {self.offset} |\n")
|
|
|
|
|
for row in rows:
|
|
|
|
|
result += " | ".join(row) + " ||\n"
|
|
|
|
|
result += "table@\n"
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def html(self):
|
|
|
|
|
return self.columns()
|
|
|
|
|
|
|
|
|
|
def tex(self):
|
|
|
|
|
return self.columns()
|
2026-07-18 18:48:23 +02:00
|
|
|
|
2026-09-16 16:59:49 +02:00
|
|
|
def txt(self):
|
|
|
|
|
return self.columns()
|