Skip to content

Instantly share code, notes, and snippets.

@ctrlcctrlv
Created June 9, 2020 03:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ctrlcctrlv/74d5c5ac9599f8000394490c1ed45a83 to your computer and use it in GitHub Desktop.
Save ctrlcctrlv/74d5c5ac9599f8000394490c1ed45a83 to your computer and use it in GitHub Desktop.
FontForge "Community guidelines" authoring script
#!/usr/bin/python3
# This program outputs the "Link to this section" links, and also a table of
# contents for the "Community guidelines" wiki FontForge page.
#
# Warning: pretty hacky...
#
# Original author: Fredrick R. Brennan
# License: FontForge original BSD license
#
# Run as e.g.:
# ./manage_guidelines.py < cgs.md > /tmp/cgs.md
#
# "Never spend 6 minutes doing something by hand when you can spend 6 hours failing to automate it." ~ Zhuowei Zhang (σ╝╡σìôσüë)
import re
import sys
cg = sys.stdin.read()
lines = cg.splitlines()
# Find our generated TOC and delete it if applicable.
TOCSTART = '# Table of Contents <a name="toc"/><!-- TOCSTART -->'
TOCEND = '<!-- TOCEND -->\n'
TOCi = None
ntoc = 0
for i, line in enumerate(lines):
if not TOCSTART in line:
continue
else:
TOCi = i
j = 0
while lines[i+j+1].lstrip().startswith('*'):
ntoc+=1
j +=1
if TOCEND[:-1] in lines[i+j+1]:
ntoc+=2
if ntoc!=0:
ntoc+=1
break
if TOCi is not None:
for i in range(TOCi, TOCi+ntoc):
del lines[TOCi]
# HTML parser to find anchors and remember the last found.
from html.parser import HTMLParser
class AnchorParser(HTMLParser):
@staticmethod
def attrs_to_dict(attrs):
ret = dict()
for k, v in attrs:
if k in ret:
raise ValueError("Double attribute in tag")
ret.setdefault(k, v)
return ret
curanchor = None
parsed = 0
def handle_starttag(self, tag, attrs):
attrs = AnchorParser.attrs_to_dict(attrs)
if tag == "a" and "name" in attrs:
self.parsed+=1
self.curanchor = attrs["name"]
parser = AnchorParser()
# Write out the "quick links" and populate the TOC "tree" (which is really not a tree data structure per se)
anchors = list()
outlines = list() # We don't want to output in the loop as it might error.
tree = list()
firstheading = None
QUICKLINK = '<!-- QUICKLINK -->'
for i, line in enumerate(lines):
if not QUICKLINK in line: outlines.append(line)
if not re.match(r'^#', line) or TOCSTART in line:
continue
parsedb4 = parser.parsed
parser.feed(line)
if parser.parsed == parsedb4 or parser.curanchor == None:
raise ValueError("No anchor in heading `{}`, add one.".format(line[line.index(' ')+1:]))
if parser.curanchor in anchors:
raise ValueError("Document has two anchors with name `{}`, cannot continue. Rename one of them.".format(parser.curanchor))
outlines.append("<sup>[Quick link to section](#{})</sup>".format(parser.curanchor)+QUICKLINK)
# Add a blank line after link if one isn't already there, and doing so is "appropriate"
if (i+1 <= len(lines)-1) and ((len(lines[i+1].strip()) > 0 and lines[i+1][0] == '<') ^ \
(len(lines[i+1].strip()) > 0)):
outlines.append("")
tree.append((line.count('#', 0, line.index(' ')), parser.curanchor, line[line.index(' ')+1:line.index('<')]))
if firstheading is None: firstheading = i
anchors.append(parser.curanchor)
# Write our TOC
toc = list()
toc.append(TOCSTART)
for level, anchor, name in tree:
toc.append("""{} <a href="#{}">{}</a>""".format(' '*2*(level-1)+'*', anchor, name))
toc.append(TOCEND)
if firstheading is None:
raise ValueError("No headings, add one.")
outlines = outlines[:firstheading] + toc + outlines[firstheading:]
for line in outlines:
print(line)
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment