gem2html.py (3419B)
1 import random, functools, os 2 from html import escape 3 4 _rand_n = lambda: functools.reduce(lambda x, y: (x<<8)+y,os.urandom(4)) 5 6 ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz" 7 USED_IDS = set() 8 def rand_id(): 9 n = _rand_n() 10 id = "" 11 while n>0: 12 n, index = divmod(n,len(ALPHABET)) 13 id = ALPHABET[index]+id 14 if id in USED_IDS: return rand_id() 15 return id 16 17 def gem2html(content,link_callback=lambda url, text: (url, text)): 18 lines = content.splitlines() 19 out = """<!DOCTYPE html> 20 <html> 21 <head> 22 <meta charset="UTF-8"> 23 <meta name="viewport" content="width=device-width, initial-scale=1"> 24 <link rel="stylesheet" href="gem.css"></head> 25 <body> 26 27 """ 28 pre = False 29 pre_alt = False 30 set_title = False 31 for line in lines: 32 if pre: 33 if line[:3]=="```": 34 pre=False 35 out+="</pre>\n" 36 if pre_alt: 37 out+="</figure>\n" 38 pre_alt=False 39 else: 40 out+=escape(line)+"\n" 41 else: 42 if line[:3]=="```": 43 if len(line)>3: 44 cap_id = rand_id() 45 out+="<figure role='img' aria-captionedby='{0}'><figcaption id='{0}' style='clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px;'>{1}</figcaption>\n".format(cap_id,escape(line[3:])) 46 pre_alt = True 47 pre = True 48 out+="<pre>\n" 49 elif line.startswith("#"): 50 if line[:3]=="###": 51 out+="<h3>{}</h3>".format(escape(line[3:].strip())) 52 elif line[:2]=="##": 53 out+="<h2>{}</h2>".format(escape(line[2:].strip())) 54 elif line[:1]=="#": 55 out+="<h1>{}</h1>".format(escape(line[1:].strip())) 56 if not set_title: 57 out+="<title>{}</title>".format(escape(line[1:].strip())) 58 set_title = True 59 elif line.startswith("* "): 60 out += "<ul>\n<li>{}</li>\n</ul>\n".format(escape(line[1:].strip())) 61 # combine consecutive unordered list items into one unordered list 62 out = out.replace("</ul>\n<ul>\n","") 63 elif line.startswith("=>"): 64 parts = line.split(None,2) 65 try: 66 url, text = parts[1:] 67 except ValueError: 68 try: 69 url=parts[1] 70 text=parts[1] 71 except: 72 # no link content at all 73 # just put a literal => in there 74 out+="<p></p>".format(escape(parts[0])) 75 continue 76 # now comes the fun part, use the link callback to mutilate these 77 url, text = link_callback(url, text) 78 # and now render 79 out+="<p><a href='{}'>{}</a></p>".format(escape(url),escape(text)) 80 elif line.startswith(">"): 81 out+="<blockquote><p>{}</p></blockquote>".format(escape(line)) 82 else: # any other line is a text line 83 if line: 84 out+="<p>{}</p>".format(escape(line)) 85 else: 86 out+="<p><br></p>" 87 out+="</body>" 88 return out