+
+def add_table_of_themes(root):
+ try:
+ from sortify import sortify
+ except ImportError:
+ sortify = lambda x: 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 = 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):
+ """For each annotation, yields a tuple: anchor, text, html."""
+ parser = etree.HTMLParser(encoding='utf-8')
+ tree = etree.parse(html_path, parser)
+ footnotes = tree.find('//*[@id="footnotes"]')
+ re_qualifier = re.compile(ur'[^\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()
+ qualifier = None
+ match = re_qualifier.match(text_str)
+ if match:
+ qualifier = match.group(1)
+
+ yield anchor, fn_type, qualifier, text_str, html_str
+