+    
+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 = 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(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()
+
+            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