Skip to content

Instantly share code, notes, and snippets.

@nftchance
Created July 18, 2022 13:54
Show Gist options
  • Save nftchance/05ee244b8b466aac636bc8f323a53445 to your computer and use it in GitHub Desktop.
Save nftchance/05ee244b8b466aac636bc8f323a53445 to your computer and use it in GitHub Desktop.
Bionic Reading
"""
Markdown extension for utilizing Bionic Reading.
Author: nftchance
License: MIT
Developer notice: The official accessibility tool for Bionic Reading
is locked behind a $99/month plan for what is ~75 lines of code.
Feel free to yoink this all you want. (Still maintain route of
monetization but if you put this behind a paid wall I am coming for you.)
Eg:
Markdown:
The cat jumped over the brown fox.
Output without extension:
<p>The cat jumped over the brown fox.</p>
Output with extension:
<p><b>Th</b>e <b>c</b>at <b>jum</b>ped <b>ov</b>er <b>t</b>he <b>bro<//b>wn <b>f</b>ox.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
import math
import re
from markdown import Extension
from markdown.treeprocessors import Treeprocessor
from markdown.util import etree
class BionicReadingTreeProcessor(Treeprocessor):
"""
Goes through the paragraph tags and combines them into a string
allowing a single call to be made instead of a bunch of 'em.
* Has issues with special characters. If using with SmartyPants/
something that messes with special characters, Bionic Reading needs
to be processed first as special characters struggle.
"""
FIXATION_TOP = 6
FIXATION_POINTS = 10
SPACE = " "
PREV_ELEMENT = None
def buildBionicWord(self, p, word, saccade_skip):
# if a space is needed add it to the previous element
if word == self.SPACE and self.PREV_ELEMENT:
self.PREV_ELEMENT.tail += self.SPACE
return
# calculate the fixation length
blen = math.ceil(len(word) * (
self.FIXATION_TOP - self.config['fixation']
) / self.FIXATION_POINTS)
element_type = 'b' if not saccade_skip else 'span'
# wrap the text in a bold element
b_element = etree.SubElement(
p, element_type, attrib={'class': 'b bionic'}
)
# add the bolded text
b_element.text = word[:blen]
# add the tail text
b_element.tail = word[blen:]
self.PREV_ELEMENT = b_element
return None
def buildBionicText(self, text, p):
saccade_length = 0
for word in re.split(r'(\s+)', text): # loop through every word
saccade_length -= len(word)
saccade_skip = saccade_length > 0
# building bionic word
self.buildBionicWord(
p,
word,
saccade_skip
)
# if we bioniced a piece of text, pad the coming
if not saccade_skip:
saccade_length = self.config['saccade'] - 10
return None
def run(self, root):
content_types = self.config['content_types']
contents = []
for content_type in content_types:
contents += root.findall(f".//{content_type}")
for p in contents: # loop through very element
text = p.text # copy the text value as we need it
# if we shouldn't format this text, skip
if not text or len(text) == 1: continue
# ignores the editing of KaTeX rendering pieces
if "wzxhzdk" in text: continue
p.text = "" # clear existing text we copied
# convert to bionic text
self.buildBionicText(text, p)
return None
class BionicReadingExtension(Extension):
def __init__(self, **kwargs):
# initialize configuration
self.config = {
'content_types': [
[
'h1',
'h2',
'h3',
'h4',
'h5',
'a',
'em',
'p',
'figcaption',
],
"The types of DOM elements that will be Bionic formatted. (h1,h2,h3...,a,em,p)"
],
'fixation': [
1,
"With the Fixation you define the expression of the letter combinations. You can choose a value between 1 and 5 (1,2,3, 4 or 5)"
],
'saccade': [
30,
"Controls the number of un-bolded characters between fixation points when not 10."
]
}
super(BionicReadingExtension, self).__init__(**kwargs)
def extendMarkdown(self, md, md_globals):
# add BionicReadingExtension into markdown instance
md.registerExtension(self)
extension = BionicReadingTreeProcessor(md)
extension.config = self.getConfigs()
md.treeprocessors.add('bionic_reading', extension, '_end')
def makeExtension(**kwargs):
return BionicReadingExtension(**kwargs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment