Verbatim-safe typography, ^-punctuation quoting, full-range ^UUUU^, polyglot html

Typographic transforms (---, quote pairs, ~) no longer touch verbatim
text: @c/@code/@source_listing content and ^'...'^ spans show exactly the
characters written. "^" before any punctuation character quotes it in
every target (the apostrophe excepted: ^' opens a literal span), with the
new :resolve option on @@@target declaring per-target renderings. The
^UUUU^ code-point form accepts 4-6 hex digits, the full Unicode range.
The html output and transform spellings are polyglot (XML-valid), in
preparation for an EPUB target. New suites: transform_test, character_test
(engine), typography_test (SKS).

(from dev 07ce5ea86a0a)
This commit is contained in:
2026-08-23 20:48:26 +02:00
parent d982c0d6cc
commit 37b6ba1c4f
77 changed files with 1665 additions and 1608 deletions

View File

@@ -8,7 +8,7 @@ import kutil
_novalue = '__no_value__'
class E:
void_elements = set("area base br col embed hr img input link meta param source track wbr".split())
void_elements = "area base br col embed hr img input link meta source track wbr".split()
def __init__(self, tag):
self._tag = tag
self._sty = []
@@ -17,6 +17,7 @@ class E:
self._attr = []
self.no_value = '__novalue__'
def sty(self, name, value=_novalue):
if name and value is _novalue:
self._sty.append(name.strip(';'))
@@ -49,6 +50,7 @@ class E:
self._attr.append(attr)
return self
def __str__(self):
def label(name, s):
return ' {}="{}"'.format(name, s) if s else ''
@@ -60,7 +62,16 @@ class E:
b = "\n".join(self._body)
tag = '{}{}{}{}'.format(self._tag, a, c, s).strip()
end_tag = '</{}>'.format(self._tag) if self._tag not in E.void_elements else ""
return '<{}>{}{}\n'.format(tag, b, end_tag)
no_body = self._tag in E.void_elements
if b and no_body:
raise Exception(f"Tag {self._tag} incorrectly defines a body.")
if no_body:
result = f'<{tag}/>'
else:
result = f'<{tag}>{b}{end_tag}\n' # .format(tag, b, end_tag)
if no_body:
print(result)
return result
def __repr__(self):
return self.__str__()
@@ -80,6 +91,9 @@ class E:
result = result.rstrip() + "\n"
return result
def color(rgb):
return "rgb(" + ",".join([f"{float(e)*100}%" for e in rgb.split(",")]) + ")"
def html_indent(filename):
command = "(progn (setq make-backup-files nil) (mark-whole-buffer) "
command += "(indent-region (point-min) (point-max) nil) (save-buffer))"
@@ -99,10 +113,10 @@ def page(head_elt, body, js_files=[], load_jquery=True):
def head(title, js_files=[], js_code="", css_files=[], css_code="",
include_fonts=True, google_font=[], favicon=None, load_jquery=True):
result = '<meta name="viewport" content="width=device-width, initial-scale=1">\n'
result += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n'
result = '<meta name="viewport" content="width=device-width, initial-scale=1"/>\n'
result += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\n'
if favicon:
result += f'<link rel="icon" type="image/x-icon" href="{favicon}">\n'
result += f'<link rel="icon" type="image/x-icon" href="{favicon}"/>\n'
result += "".join([E("link").attr("href", e).attr("rel", "stylesheet").str() for e in css_files])
fonts = None
css = ''