, and
13 | tags. It does not include any boilerplate such as a default
14 | or wrapping or tags. I wrote it mainly so I could insert converted
15 | gemtext into a webpage I already started using copy-paste or a templating system
16 | like Jinja. Extend it as you see fit.
17 |
18 | If you have any suggestions on how to make it better you can reach me at
19 | hunterkb@ksu.edu
20 | """
21 |
22 | # importing required libraries
23 | import contextlib
24 | import sys
25 | import re
26 |
27 | # A dictionary that maps regex to match at the beginning of gmi lines to their corresponding HTML tag names. Used by convert_single_line().
28 | tags_dict = {
29 | r"^# (.*)": "h1",
30 | r"^## (.*)": "h2",
31 | r"^### (.*)": "h3",
32 | r"^\* (.*)": "li",
33 | r"^> (.*)": "blockquote",
34 | r"^=>\s*(\S+)(\s+.*)?": "a"
35 | }
36 |
37 | # This function takes a string of gemtext as input and returns a string of HTML
38 | def convert_single_line(gmi_line):
39 | for pattern in tags_dict.keys():
40 | if match := re.match(pattern, gmi_line):
41 | tag = tags_dict[pattern]
42 | groups = match.groups()
43 | if tag == "a":
44 | href = groups[0]
45 | inner_text = groups[1].strip() if len(groups) > 1 else href
46 | return f"<{tag} href='{href}'>{inner_text}{tag}>"
47 | else:
48 | inner_text = groups[0].strip()
49 | return f"<{tag}>{inner_text}{tag}>"
50 | return f"{gmi_line}
"
51 |
52 | # smart open for stdin & stdout
53 | # credit to https://stackoverflow.com/a/17603000/15956024
54 | @contextlib.contextmanager
55 | def smart_open(file_name=None, encoding='r'):
56 | if file_name and file_name != 'STDIN' and file_name != 'STDOUT':
57 | fh = open(file_name, encoding)
58 | elif file_name == 'STDIN':
59 | fh = sys.stdin
60 | elif file_name == 'STDOUT':
61 | fh = sys.stdout
62 | else:
63 | # will never reach here
64 | pass
65 |
66 | try:
67 | yield fh
68 | finally:
69 | if fh is not sys.stdin or fh is not sys.stdout:
70 | fh.close()
71 |
72 | # Reads the contents of the input file line by line and outputs HTML. Renders text in preformat blocks (toggled by ```) as multiline tags.
73 | def main(args):
74 | input = args[1] if len(args) > 1 else "STDIN"
75 | output = args[2] if len(args) > 2 else "STDOUT"
76 |
77 | with smart_open(input) as gmi, smart_open(output) as html:
78 | preformat = False
79 | in_list = False
80 | for line in gmi:
81 | line = line.strip()
82 | if len(line):
83 | if line.startswith("```") or line.endswith("```"):
84 | preformat = not preformat
85 | repl = "" if preformat else "
"
86 | html.write(re.sub(r"```", repl, line))
87 | elif preformat:
88 | html.write(line)
89 | else:
90 | html_line = convert_single_line(line)
91 | if html_line.startswith("