+
+def add_table_of_themes(root):
+ try:
+ from sortify import sortify
+ except ImportError:
+ def sortify(x):
+ return x
+
+ book_themes = {}
+ for fragment in root.findall('.//a[@class="theme-begin"]'):
+ if not fragment.text:
+ continue
+ theme_names = [s.strip() for s in fragment.text.split(',')]
+ for theme_name in theme_names:
+ book_themes.setdefault(theme_name, []).append(fragment.get('name'))
+ book_themes = list(book_themes.items())
+ book_themes.sort(key=lambda s: sortify(s[0]))
+ themes_div = etree.Element('div', id="themes")
+ themes_ol = etree.SubElement(themes_div, 'ol')
+ for theme_name, fragments in book_themes:
+ themes_li = etree.SubElement(themes_ol, 'li')
+ themes_li.text = "%s: " % theme_name
+ for i, fragment in enumerate(fragments):
+ item = etree.SubElement(themes_li, 'a', href="#%s" % fragment)
+ item.text = str(i + 1)
+ item.tail = ' '
+ root.insert(0, themes_div)
+
+
+def extract_annotations(html_path):
+ """Extracts annotations from HTML for annotations dictionary.
+
+ For each annotation, yields a tuple of:
+ anchor, footnote type, valid qualifiers, text, html.
+
+ """
+ from .fn_qualifiers import FN_QUALIFIERS
+
+ parser = etree.HTMLParser(encoding='utf-8')
+ tree = etree.parse(html_path, parser)
+ footnotes = tree.find('//*[@id="footnotes"]')
+ re_qualifier = re.compile(r'[^\u2014]+\s+\(([^\)]+)\)\s+\u2014')
+ if footnotes is not None:
+ for footnote in footnotes.findall('div'):
+ fn_type = footnote.get('class').split('-')[1]
+ anchor = footnote.find('a[@class="annotation"]').get('href')[1:]
+ del footnote[:2]
+ footnote.text = None
+ if len(footnote) and footnote[-1].tail == '\n':
+ footnote[-1].tail = None
+ text_str = etree.tostring(footnote, method='text', encoding='unicode').strip()
+ html_str = etree.tostring(footnote, method='html', encoding='unicode').strip()
+
+ match = re_qualifier.match(text_str)
+ if match:
+ qualifier_str = match.group(1)
+ qualifiers = []
+ for candidate in re.split('[;,]', qualifier_str):
+ candidate = candidate.strip()
+ if candidate in FN_QUALIFIERS:
+ qualifiers.append(candidate)
+ elif candidate.startswith('z '):
+ subcandidate = candidate.split()[1]
+ if subcandidate in FN_QUALIFIERS:
+ qualifiers.append(subcandidate)
+ else:
+ qualifiers = []
+
+ yield anchor, fn_type, qualifiers, text_str, html_str