X-Git-Url: https://git.mdrn.pl/librarian.git/blobdiff_plain/6c0f71f581d5b8d9a519a6645f571f54b38afca8..a04f11baee3eb7d090867c2d5639a120ec3217b8:/librarian/html.py diff --git a/librarian/html.py b/librarian/html.py index 5f832e3..85b9003 100644 --- a/librarian/html.py +++ b/librarian/html.py @@ -4,13 +4,12 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # import os -import cStringIO import re +import cStringIO import copy from lxml import etree -from librarian.parser import WLDocument -from librarian import XHTMLNS, ParseError +from librarian import XHTMLNS, ParseError, OutputFile from librarian import functions from lxml.etree import XMLSyntaxError, XSLTApplyError @@ -30,9 +29,8 @@ def get_stylesheet(name): def html_has_content(text): return etree.ETXPath('//p|//{%(ns)s}p|//h1|//{%(ns)s}h1' % {'ns': str(XHTMLNS)})(text) -def transform(input, output_filename=None, is_file=True, \ - parse_dublincore=True, stylesheet='legacy', options={}, flags=None): - """Transforms file input_filename in XML to output_filename in XHTML. +def transform(wldoc, stylesheet='legacy', options=None, flags=None): + """Transforms the WL document to XHTML. If output_filename is None, returns an XML, otherwise returns True if file has been written,False if it hasn't. @@ -43,12 +41,9 @@ def transform(input, output_filename=None, is_file=True, \ style_filename = get_stylesheet(stylesheet) style = etree.parse(style_filename) - if is_file: - document = WLDocument.from_file(input, True, \ - parse_dublincore=parse_dublincore) - else: - document = WLDocument.from_string(input, True, \ - parse_dublincore=parse_dublincore) + document = copy.deepcopy(wldoc) + del wldoc + document.swap_endlines() if flags: for flag in flags: @@ -56,23 +51,20 @@ def transform(input, output_filename=None, is_file=True, \ document.clean_ed_note() + if not options: + options = {} result = document.transform(style, **options) del document # no longer needed large object :) if html_has_content(result): add_anchors(result.getroot()) + add_table_of_themes(result.getroot()) add_table_of_contents(result.getroot()) - if output_filename is not None: - result.write(output_filename, method='html', xml_declaration=False, pretty_print=True, encoding='utf-8') - else: - return result - return True + return OutputFile.from_string(etree.tostring(result, method='html', + xml_declaration=False, pretty_print=True, encoding='utf-8')) else: - if output_filename is not None: - return False - else: - return "" + return None except KeyError: raise ValueError("'%s' is not a valid stylesheet.") except (XMLSyntaxError, XSLTApplyError), e: @@ -143,14 +135,17 @@ def extract_fragments(input_filename): fragment = Fragment(id=element.get('fid'), themes=element.text) # Append parents - if element.getparent().get('id', None) != 'book-text': - parents = [element.getparent()] - while parents[-1].getparent().get('id', None) != 'book-text': - parents.append(parents[-1].getparent()) - - parents.reverse() - for parent in parents: - fragment.append('start', parent) + parent = element.getparent() + parents = [] + while parent.get('id', None) != 'book-text': + cparent = copy.deepcopy(parent) + cparent.text = None + parents.append(cparent) + parent = parent.getparent() + + parents.reverse() + for parent in parents: + fragment.append('start', parent) open_fragments[fragment.id] = fragment @@ -173,7 +168,8 @@ def extract_fragments(input_filename): # Process all elements except begin and end else: # Omit annotation tags - if len(element.get('name', '')) or element.get('class', '') == 'annotation': + if (len(element.get('name', '')) or + element.get('class', '') in ('annotation', 'anchor')): if event == 'end' and element.tail: for fragment_id in open_fragments: open_fragments[fragment_id].append('text', element.tail) @@ -185,25 +181,22 @@ def extract_fragments(input_filename): def add_anchor(element, prefix, with_link=True, with_target=True, link_text=None): + parent = element.getparent() + index = parent.index(element) + if with_link: if link_text is None: link_text = prefix anchor = etree.Element('a', href='#%s' % prefix) anchor.set('class', 'anchor') anchor.text = unicode(link_text) - if element.text: - anchor.tail = element.text - element.text = u'' - element.insert(0, anchor) + parent.insert(index, anchor) if with_target: anchor_target = etree.Element('a', name='%s' % prefix) anchor_target.set('class', 'target') anchor_target.text = u' ' - if element.text: - anchor_target.tail = element.text - element.text = u'' - element.insert(0, anchor_target) + parent.insert(index, anchor_target) def any_ancestor(element, test): @@ -230,18 +223,27 @@ def add_anchors(root): counter += 1 +def raw_printable_text(element): + working = copy.deepcopy(element) + for e in working.findall('a'): + if e.get('class') in ('annotation', 'theme-begin'): + e.text = '' + return etree.tostring(working, method='text', encoding=unicode).strip() + + def add_table_of_contents(root): sections = [] counter = 1 for element in root.iterdescendants(): if element.tag in ('h2', 'h3'): - if any_ancestor(element, lambda e: e.get('id') in ('footnotes',) or e.get('class') in ('person-list',)): + if any_ancestor(element, lambda e: e.get('id') in ('footnotes', 'nota_red') or e.get('class') in ('person-list',)): continue + element_text = raw_printable_text(element) if element.tag == 'h3' and len(sections) and sections[-1][1] == 'h2': - sections[-1][3].append((counter, element.tag, ''.join(element.xpath('text()')), [])) + sections[-1][3].append((counter, element.tag, element_text, [])) else: - sections.append((counter, element.tag, ''.join(element.xpath('text()')), [])) + sections.append((counter, element.tag, element_text, [])) add_anchor(element, "s%d" % counter, with_link=False) counter += 1 @@ -263,3 +265,54 @@ def add_table_of_contents(root): root.insert(0, toc) + +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 +