import os import re import sys import textwrap sys.path.append(f'{os.environ.get("KLAMMERTEXT_HOME")}/sks/kutil') 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()) def __init__(self, tag): self._tag = tag self._sty = [] self._cls = [] self._body = [] self._attr = [] self.no_value = '__novalue__' def sty(self, name, value=_novalue): if name and value is _novalue: self._sty.append(name.strip(';')) elif value and value is not _novalue: if type(value) is list: value = " ".join(value) self._sty.append('{}:{}'.format(name, value)) return self def cls(self, c): if c: self._cls.append(c) return self def body(self, b, newline=True): b = str(b) #print("B:", b) if newline and (b and b[0] == '<' or '\n' in b or len(b) > 72): b = '\n' + b + '\n' self._body.append(b) return self def attr(self, name, value): self._attr.append('{}="{}"'.format(name, value)) return self def data(self, name, value="true"): attr = 'data-{}'.format(name) attr = '{}="{}"'.format(attr, value) self._attr.append(attr) return self def __str__(self): def label(name, s): return ' {}="{}"'.format(name, s) if s else '' c = label('class', " ".join(self._cls)) s = "; ".join(self._sty).strip(' ;') s = label('style', s + ';' if s else '') a = " ".join(self._attr) a = " " + a.strip() if a.strip() else "" 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) def __repr__(self): return self.__str__() def __add__(self, e): return str(self) + '\n' + str(e) def str(self, newline=True): result = str(self) #print(f"begin: |{result}|") if newline is None: #result = result.strip("\n") result = re.sub(">\n", ">", result) #print("newline: None") #print(f"str: |{result}|\n") elif newline: result = result.rstrip() + "\n" return result def html_indent(filename): command = "(progn (setq make-backup-files nil) (mark-whole-buffer) " command += "(indent-region (point-min) (point-max) nil) (save-buffer))" os.system(f'emacs -nw -q --batch {filename} --eval "{command}" --kill 2> /dev/null') def page(head_elt, body, js_files=[], load_jquery=True): if True or js_files and load_jquery: body += E("script").attr( "src", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js").str() for j in js_files: body += E("script").attr("src", j).str() #body_elt = E("body").attr("id", "body").body(f"\n\n{body}\n\n").str().strip() body_elt = E("body").body(f"\n\n{body}\n\n").str().strip() result = "\n" + E("html").attr("lang", "en").body(head_elt + body_elt).str() return result def head(title, js_files=[], js_code="", css_files=[], css_code="", include_fonts=True, google_font=[], favicon=None, load_jquery=True): result = '\n' result += '\n' if favicon: result += f'\n' result += "".join([E("link").attr("href", e).attr("rel", "stylesheet").str() for e in css_files]) fonts = None css = '' if not css_files and not css_code and include_fonts: fonts, css = default_fonts() fonts = google_font + fonts if fonts: result += E("link").attr("rel", "preconnect").attr("href", "https://fonts.gstatic.com").str() #font = "|".join(google_font) for font in fonts: result += E("link").attr("href", f"https://fonts.googleapis.com/css?family={font}&display=swap") \ .attr("rel", "stylesheet").str() #.attr("type", "text/css").str() if css + css_code: result += E("style").body(css + css_code).str() if js_files and load_jquery: result += E("script").attr( "src", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js").str() for j in js_files: result += E("script").attr("src", j).str() result += E("title").body(title).str().strip() result = E("head").body(result).str() return result def default_fonts(): def font_name(spec): return re.compile('[&|:]').split(re.sub(r'\+', ' ', spec))[0] #serif = "PT+Serif:ital,wght@0,400;0,700;1,400" #serif = "Manuale:ital,wght^@0,400;0,600;1,400" serif = "Gentium+Book+Basic:ital,wght^@0,400;0,700;1,400" monospace = "Roboto+Mono:ital,wght^@0,400;0,600;1,400" sans = "PT+Sans:ital,wght^@0,400;0,700;1,400" fonts = [serif, sans, monospace] css = f'body {{ font-family: "{font_name(serif)}", serif; margin: 3rem; font-size: 14px; }}\n' css += f'tt {{ font-family: "{font_name(monospace)}", sans-serif; font-size: 13px; }}\n' css += f'.sans {{ font-family: "{font_name(sans)}", monospace; font-size: 14px; }}\n' css += f'h1, h2, h3, h4 {{ font-family: "{font_name(sans)}", monospace; font-size: 20px; }}' return fonts, css def css_reldir(): return "css.ktdir" """ def css_basenames(): kdir = kutil.klammertext_dir() result = [] for base, sks_dir in kutil.sks_dirs(): css_filename = f'{sks_dir}/{base}.css' if os.path.exists(css_filename): result.append(d) return result def css_source_files(): result = [] kdir = kutil.klammertext_dir() for basename in css_basenames(): result.append([basename, f"{kdir}/sks/{basename}/{basename}.css"]) return result """ def css(styles, use_sks=False): code = f'' if styles else '' files = [] if use_sks: for basename, filename in kutil.sks_files_of_type("css"): files.append(f"{css_reldir()}/{basename}.css") """ kdir = kutil.klammertext_dir() css_files = [] for basename in css_basenames(): files.append(f'{css_reldir()}/{basename}.css') """ return code, files def element_tag(element): tag_match = re.compile(r"<(\w+).*", re.S).match(str(element)) if not tag_match: raise Exception(f"No tag found in {element}") else: return tag_match.group(1) # 1. Relationship of element to caption # 2. Relationship of element with/without caption to page def font_class(font): return {"r" : "", "i" : "ritalic", "t" : "monospace", "s" : "sanserif"}[font] def add_caption(element, caption_label, number, caption_text, font_symbol="i", hpos="center", side="bottom", as_string=True, font_size=.9): tag = element_tag(element) # Caption caption = "" if number: caption = kutil.caption_marker(caption_label, caption_text) elif caption_text: caption = caption_text if caption: # caption = kutil.protect_klammertext_special_characters(caption) import font caption = font.html_fontify(caption, font_symbol, font_size) caption = E("div").cls("caption").body(caption, newline=False) if caption_text and side == "bottom" or side == "top": caption.cls("caption_text") if caption: if side == "left": element.cls("lgap") caption.cls("rgap") elif side == "right": element.cls("rgap") caption.cls("lgap") elif side == "bottom": element.cls("bgap") elif side == "top": element.cls("tgap") if side in {"left", "top"}: element = str(caption) + "\n" + str(element) else: #element += "\n" + "\n".join(textwrap.wrap(str(caption), 80)) element += "\n" + re.sub("\n", " ", str(caption)) #print("-"*80) #print(element) #print("-"*80) element = E("div").cls("caption_" + side).attr("data-label", caption_label).body(element) result = element result = E("div").cls("hpos_" + hpos).body(element) if hpos != "center": result = result.cls("hpos_margin") if number: result.cls("element_container") if caption and hpos != "none": if side == "bottom": result.cls("mbot") elif side == "top": result.cls("mtop") if as_string: result = str(result) + "\n" return result if __name__ == '__main__': elt = E("img").attr("src", "tree.jpg") print(elt) sys.exit(0) import re print(css("", True)) sys.exit(0) def make_pars(s): s = re.compile(r';(.*?)\.', re.S).sub(r';\1.', s) s = re.sub('--', '—', s) s = re.compile(r'"(\s)', re.S).sub(r'”\1', s) s = re.compile('"', re.S).sub('“', s) s = re.compile("'", re.S).sub('’', s) s = re.sub("whale", "whale", s) s = re.sub("Queequeg", "Queegueg", s) s = re.compile(r"(CHAPTER.*?Rope)\.", re.S).sub(r'\1', s) s = re.sub(" and ", ' and ', s) pars = re.compile('\n\n+').split(s) result = "\n\n".join(["

{}

".format(e) for e in pars]) return result google_font, css = default_fonts() with open("../../../moby/moby-072.txt") as fp: txt = make_pars(fp.read()) hd = head("Moby Dick - Chapter 72", css_code=css, google_font=google_font) pg = page(hd, txt) filename = "chapter_72.html" with open(filename, "w") as fp: fp.write(pg) #html_indent(filename)