示例#1
0
文件: nemeth.py 项目: dusek/lcg
def _export_munder(node, exporter, context, variables, **kwargs):
    base, under = _child_nodes(node)
    result = (_export(base, exporter, context, variables, **kwargs) + _Braille('⠩') +
            _export(under, exporter, context, variables, **kwargs))
    if not variables.get('no-under-boundaries'):
        result = _modifier(variables) + result + _Braille('⠻')
    return result
示例#2
0
文件: nemeth.py 项目: dusek/lcg
def _export_mover(node, exporter, context, variables, **kwargs):
    base, over = _child_nodes(node)
    exported_base = _export(base, exporter, context, variables, **kwargs)
    if ((over.tag == 'mo' and over.text.strip() == '¯' and
         base.tag in ('mi', 'mn',) and len(base.text.strip()) == 1)):
        return exported_base + _Braille('⠱')
    return (_modifier(variables) + exported_base + _Braille('⠣') +
            _export(over, exporter, context, variables, **kwargs) + _Braille('⠻'))
示例#3
0
文件: nemeth.py 项目: dusek/lcg
def _mroot(base, index, exporter, context, variables, **kwargs):
    level = variables.get('root-level', 0)
    repeater = _Braille('⠨' * level)
    prefix = _Braille('⠜')
    if index is not None:
        prefix = _Braille('⠣') + _export(index, exporter, context, variables, **kwargs) + prefix
    prefix = repeater + prefix
    suffix = repeater + _Braille('⠻')
    with variables.let('root-level', level + 1):
        if index is None:
            exported_base = _child_export(base, exporter, context, variables, **kwargs)
        else:
            exported_base = _export(base, exporter, context, variables, **kwargs)
    return prefix + exported_base + suffix
示例#4
0
文件: nemeth.py 项目: dusek/lcg
def _export_msubsup(node, exporter, context, variables, **kwargs):
    base, sub, sup = _child_nodes(node)
    prime = None
    sub_only = False
    if sup.tag == 'mo' and sup.text.strip() in _primes:
        prime = _nemeth_operators[sup.text.strip()]
        sub_only = True
    elif sup.tag == 'mrow':
        sup_children = _child_nodes(sup)
        if ((sup_children and sup_children[0].tag == 'mo' and
             sup_children[0].text.strip() in _primes)):
            prime = _nemeth_operators[sup_children[0].text.strip()]
            if len(sup_children) > 1:
                sup.remove(sup_children[0])
            else:
                sub_only = True
    if prime is not None:
        base.braille = _export(base, exporter, context, variables, **kwargs) + _Braille(prime)
    from xml.etree import ElementTree
    node.clear()
    if sub_only:
        node.tag = 'msub'
        node.append(base)
        node.append(sub)
        return _export_msub(node, exporter, context, variables, **kwargs)
    else:
        node.tag = 'msup'
        ElementTree.SubElement(node, 'msub')
        node.append(sup)
        c = node.getchildren()[0]
        c.append(base)
        c.append(sub)
        return _export_msup(node, exporter, context, variables, **kwargs)
示例#5
0
文件: nemeth.py 项目: dusek/lcg
def _export_mn(node, exporter, context, variables, **kwargs):
    text = _node_value(node).strip()
    prefix = ''
    style_applied = False
    with _style(node, variables):
        style = variables.get('style', '')
        if style.find('bold') >= 0:
            prefix += '⠸'
            style_applied = True
        if style.find('italic') >= 0:
            prefix += '⠨'
            style_applied = True
        if text and text[0] == '-':
            prefix += '⠤'
            text = text[1:]
        if style_applied:
            prefix += '⠼'
        if variables.get('enclosed-list') != 'yes':
            prefix += _CONDITIONAL_NUM_PREFIX
        if context.lang() == 'cs':
            text = text.replace(',', '.')
        translated = prefix + string.join([_nemeth_numbers[c] for c in text], '')
    variables.set('enclosed-list', 'no')
    hyphenation = string.join([exporter.HYPH_NEMETH_WS if c == '⠀' else exporter.HYPH_NEMETH_NUMBER
                               for c in translated], '')
    return _Braille(translated, hyphenation)
示例#6
0
文件: nemeth.py 项目: dusek/lcg
def _export_mtable(node, exporter, context, variables, **kwargs):
    matrix = _Braille(_MATRIX_START)
    start = variables.get('matrix-start', _Braille(''))
    end = variables.get('matrix-end', _Braille(''))
    rows = _child_nodes(node)
    n_columns = 0
    for r in rows:
        n_columns = max(n_columns, len(_child_nodes(r)))
    with variables.let('matrix-n-columns', n_columns):
        for r in rows:
            matrix += start
            matrix += _export(r, exporter, context, variables, **kwargs)
            matrix += end
            matrix.append('\n')
    matrix.append(_MATRIX_END)
    return matrix
示例#7
0
文件: nemeth.py 项目: dusek/lcg
def _export_mtr(node, exporter, context, variables, **kwargs):
    n_columns = variables.get('matrix-n-columns')
    cells = _child_nodes(node)
    from xml.etree import ElementTree
    while len(cells) < n_columns:
        ElementTree.SubElement(node, 'mtd')
    cells = _child_nodes(node)
    row = _Braille('')
    for c in cells:
        row += _export(c, exporter, context, variables, **kwargs)
        row.append(_MATRIX_SEPARATOR)
    if row.text()[0] == _CONDITIONAL_NUM_PREFIX:
        row = _Braille('⠼' + row.text()[1:])
    elif row.text()[:2] == '⠤' + _CONDITIONAL_NUM_PREFIX:
        row = _Braille('⠤⠼' + row.text()[2:])
    return row
示例#8
0
文件: nemeth.py 项目: dusek/lcg
def _text_export(text, exporter, context, variables, node=None, plain=False):
    with _style(node, variables):
        # liblouis doesn't handle typeforms and letter prefixes (correctly) in
        # Nemeth so we can't relay that to it
        prefix = suffix = ''
        style = variables.get('style', '')
        if style.find('bold') >= 0:
            prefix += '⠸'
        if style.find('italic') >= 0:
            prefix += '⠨'
        if ((style and style != 'normal' and not plain and
             text and all(c in string.ascii_letters for c in text))):
            prefix += '⠰'
        elif (not style and text and all(c in string.ascii_letters for c in text) and
              variables.get('enclosed-list') != 'yes' and
              variables.get('direct-delimiters') != 'yes' and
              variables.get('no-letter-prefix') != 'yes' and
              (len(text) == 1 or text in ('cd',))): # short-form combinations
            prefix = _SINGLE_LETTER_START + prefix
            suffix += _SINGLE_LETTER_END
        else:
            if text in _signs_of_shape or text in _math_comparison_operators:
                suffix += _SINGLE_LETTER_KILLER_SUFFIX
            if text in _signs_of_shape_and_omission or text in _math_comparison_operators:
                prefix = _SINGLE_LETTER_KILLER_PREFIX + prefix
        lang = 'en' if plain else 'nemeth'
        braille = _nemeth_texts.get(text)
        if braille is None:
            braille = exporter.text(context, text, lang=lang).text().strip(_braille_whitespace)
        if prefix:
            braille = prefix + braille
        if suffix:
            braille += suffix
    return _Braille(braille)
示例#9
0
文件: nemeth.py 项目: dusek/lcg
def _export_msup(node, exporter, context, variables, **kwargs):
    base, index = _child_nodes(node)
    if index.tag == 'mo' and index.text.strip() in _primes:
        return (_export(base, exporter, context, variables, **kwargs) +
                _Braille(_nemeth_operators[index.text.strip()]))
    if index.tag == 'mrow':
        index_children = _child_nodes(index)
        if ((index_children and index_children[0].tag == 'mo' and
             index_children[0].text.strip() in _primes)):
            base.braille = (_export(base, exporter, context, variables, **kwargs) +
                            _Braille(_nemeth_operators[index_children[0].text.strip()]))
            if len(index_children) > 1:
                index.remove(index_children[0])
            else:
                return base.braille
    return __export_subsup('⠘', node, exporter, context, variables, **kwargs)
示例#10
0
文件: nemeth.py 项目: dusek/lcg
def _export_msline(node, exporter, context, variables, **kwargs):
    variables.set('msline-present', True)
    width = variables.get('ms-max-width')
    if width is None:
        text = ''
    else:
        text = '⠒' * (width)
    return _Braille(text)
示例#11
0
文件: nemeth.py 项目: dusek/lcg
def _export_mfrac(node, exporter, context, variables, **kwargs):
    numerator, denominator = _child_nodes(node)
    exported_numerator = _export(numerator, exporter, context, variables, **kwargs)
    exported_denominator = _export(denominator, exporter, context, variables, **kwargs)
    if _attribute(node, 'linethickness') == '0':
        # something like binomical coefficient
        return exported_numerator + _Braille('⠩') + exported_denominator
    left_node = variables.get('left-node')
    if left_node is not None and left_node.tag == 'mn':
        opening = _Braille('⠸⠹')
        closing = _Braille('⠸⠼')
        line = _Braille('⠌')
    else:
        def fraction_level(node):
            level = 0
            for c in _child_nodes(node):
                if c.tag not in ('msub', 'msup'):
                    level = max(level, fraction_level(c))
            if node.tag == 'mfrac':
                level += 1
            return level
        level = fraction_level(node) - 1
        if level > 2:
            raise Exception("Overcomplex fraction")
        opening = _Braille('⠠' * level + '⠹')
        closing = _Braille('⠠' * level + '⠼')
        line = _Braille('⠠' * level + '⠌')
    return opening + exported_numerator + line + exported_denominator + closing
示例#12
0
文件: nemeth.py 项目: dusek/lcg
def _export_mspace(node, exporter, context, variables, **kwargs):
    # Just basic support
    width = _attribute(node, 'width', default='0')
    try:
        n = int(width)
    except ValueError:
        if width.endswith('em') or width.endswith('ex'):
            n = int(width[:-2])
        else:
            raise
    return _Braille('⠀' * n, '4' * n)
示例#13
0
文件: nemeth.py 项目: dusek/lcg
def _child_export(node, exporter, context, variables, separators=None, **kwargs):
    braille = _Braille('', '')
    children = _child_nodes(node)
    # Check for Enclosed List (simplified)
    enclosed_list = None
    if len(children) >= 5:
        first = children[0]
        last = children[-1]
        if ((first.tag == 'mo' and first.text.strip() in ('(', '[', '{',) and
             last.tag == 'mo' and last.text.strip() in (')', ']', '}',) and
             all([c.text.strip() not in ('', ';', '.')
                  for c in children if c.tag == 'mo']) and
             all([c.tag != 'mspace' for c in children]) and
             node.find('mtext') is None and
             all([c.text.strip() not in _math_comparison_operators
                  for c in node.findall('.//mo')]))):
            enclosed_list = 'yes'
    # Check for direct contact with opening and closing group signs
    direct_delimiters = None
    if len(children) == 3:
        first = children[0]
        last = children[-1]
        content = children[1]
        while content.tag == 'mrow' and len(_child_nodes(content)) == 1:
            content = _child_nodes(content)[0]
        if ((first.tag == 'mo' and first.text.strip() in ('(', '[', '{',) and
             last.tag == 'mo' and last.text.strip() in (')', ']', '}',) and
             content.tag == 'mi')):
            direct_delimiters = 'yes'
    # Export
    left_node = None
    for i in range(len(children)):
        n = children[i]
        with variables.xlet(('enclosed-list', enclosed_list),
                            ('direct-delimiters', direct_delimiters),
                            ('left-node', left_node)):
            braille = braille + _export(n, exporter, context, variables)
        left_node = n
    return braille
示例#14
0
文件: nemeth.py 项目: dusek/lcg
def _op_export(operator, exporter, context, variables, node=None):
    op_braille = _nemeth_operators.get(operator)
    hyphenation = None
    if op_braille is None:
        op_braille = _text_export(operator, exporter, context, variables, node=node).text()
        # If liblouis translation returns something like character code
        # on unknown characters, we try to identify and handle such a
        # situation here.
        if op_braille is None or '⠈⠀⠭' in op_braille or '⡳' in op_braille:
            op_braille = string.join([c for c in op_braille if c != '⡳'], '')
            op_braille = exporter.braille_unknown_char(op_braille, operator)
            hyphenation = exporter.HYPH_NO * len(op_braille)
        elif operator in _math_comparison_operators:
            _comparison(operator, op_braille)
            op_braille = _nemeth_operators[operator]
            hyphenation = None
    if hyphenation is None:
        hyphenation = exporter.HYPH_NO * len(op_braille)
    if op_braille[0] == '⠀' and hyphenation[0] == exporter.HYPH_NO:
        hyphenation = exporter.HYPH_NEMETH_WS + hyphenation[1:]
    if op_braille[-1] == '⠀' and hyphenation[-1] == exporter.HYPH_NO:
        hyphenation = hyphenation[:-1] + exporter.HYPH_NEMETH_WS
    return _Braille(op_braille, hyphenation)
示例#15
0
文件: nemeth.py 项目: dusek/lcg
def __export_subsup(indicator, node, exporter, context, variables, **kwargs):
    base, index = _child_nodes(node)
    base_node = _child_nodes(base)[0] if base.tag == 'msup' else base
    base_tag = base_node.tag
    base_text = (base_node.text or '').strip()
    subsup = variables.get('subsup', _Braille(''))
    new_subsup = subsup
    indicate = True
    if indicator == '⠰' and not subsup.text() and index.tag == 'mn':
        index_text = index.text.strip()
        if index_text and index_text[0] != '-':
            if ((base_node.tag == 'mi' and
                 (len(base_text) == 1 or base_text in _function_names + ('Na',)))):
                indicate = False
            elif base_node.tag == 'mo' and base_text in '∑∏':
                indicate = False
    if indicate:
        new_subsup += _Braille(indicator)
    for n in _child_nodes(index):
        if n.tag == 'mo' and n.text.strip() == ',':
            n.braille = _Braille('⠪')
    if (((not new_subsup) or
         (base_tag == 'mo' and base_text in _signs_of_shape) or
         (base_tag == 'mi' and base_text in _function_names))):
        terminator = _Braille('')
    elif subsup:
        terminator = subsup
    else:
        terminator = _Braille(_END_SUBSUP)
    with variables.let('no-letter-prefix', 'yes'): # probably not *completely* correct
        exported_base = _export(base, exporter, context, variables, **kwargs)
        if exported_base.text().endswith(_END_SUBSUP):
            exported_base = _Braille(exported_base.text()[:-1], exported_base.hyphenation()[:-1])
        with variables.let('subsup', new_subsup):
            exported_index = _export(index, exporter, context, variables, **kwargs)
        if not indicate:
            exported_index = _Braille(_IMPLICIT_SUBSCRIPT) + exported_index
        return exported_base + new_subsup + exported_index + terminator
示例#16
0
文件: nemeth.py 项目: dusek/lcg
def _export_mstack(node, exporter, context, variables, **kwargs):
    rows = []
    for n in _child_nodes(node):
        if n.tag == 'msgroup':
            group_rows = _child_nodes(n)
            shift = int(_attribute(n, 'shift', default='0'))
            if shift > 0:
                rows.append((0, shift,))
            elif shift < 0:
                rows.append((((len(group_rows) - 1) * shift), shift,))
            rows += group_rows
            if shift:
                rows.append('end-shift')
        else:
            rows.append(n)
    widths_1 = [0]
    widths_2 = None
    pattern = ''
    extra_width_1 = [0]
    extra_width_2 = [0]
    widths = widths_1
    extra_width = extra_width_1
    addition_or_subtraction = False
    shift = None
    shift_inc = 0
    with variables.let('msline-present', False):
        for r in rows:
            if isinstance(r, tuple):
                shift, shift_inc = r
                continue
            if r == 'end-shift':
                shift = None
                continue
            exported = _export(r, exporter, context, variables, **kwargs).text()
            if exported and exported[0] in '⠬⠤':
                exported = exported[1:]
                extra_width[0] = 1
                addition_or_subtraction = True
            if exported and exported[0] == _CONDITIONAL_NUM_PREFIX:
                exported = exported[1:]
            for op in ('⠈⠡', '⠨'):
                if exported.startswith(op + _CONDITIONAL_NUM_PREFIX):
                    n = len(op)
                    exported = exported[:n] + exported[n + 1:]
            # Just very simplified row handling: We ignore all the MathML
            # attributes and we assume that: 1. no row has got any special
            # separator pattern suffix not present in other rows; 2. no two
            # rows have distinct non-empty separator pattern prefixes.
            parts = []
            p = ''
            i = j = 0
            for op in ('⠈⠡',):
                if exported.startswith(op):
                    i = len(op)
            if shift:
                exported += '⠀' * shift
                shift += shift_inc
            l = len(exported)
            while True:
                while i < l and exported[i] in '⠴⠂⠆⠒⠲⠢⠖⠶⠦⠔':
                    i += 1
                parts.append(exported[j:i])
                if i == l:
                    break
                p += exported[i]
                i += 1
                j = i
            if pattern is not None and not pattern.endswith(p):
                if p.endswith(pattern):
                    widths[0:0] = [0] * (len(p) - len(pattern))
                    pattern = p
                else:
                    pattern = None
            for i in range(-1, -len(p) - 2, -1):
                widths[i] = max(widths[i], len(parts[i]))
            if widths_2 is None and variables.get('msline-present'):
                widths = widths_2 = copy.copy(widths_1)
                extra_width = extra_width_2
        if pattern is None:
            if addition_or_subtraction:
                raise Exception("Non-matching mstack rows")
            pattern = ''
        max_width = (max(sum(widths_1) + extra_width_1[0], sum(widths_2) + extra_width_2[0]) +
                     len(pattern))
        msline_present = variables.get('msline-present')
        if msline_present:
            max_width += 2
    widths_1 = [0] * (len(widths_2) - len(widths_1)) + widths_1
    widths = widths_1
    shift = None
    result = ''
    with variables.xlet(('ms-widths', widths),
                        ('ms-pattern', pattern),
                        ('ms-max-width', max_width),
                        ('msline-present', False),):
        for r in rows:
            if isinstance(r, tuple):
                shift, shift_inc = r
                continue
            if r == 'end-shift':
                shift = None
                continue
            exported = _export(r, exporter, context, variables, **kwargs).text()
            if exported and exported[0] == _CONDITIONAL_NUM_PREFIX:
                exported = exported[1:]
            if shift is not None:
                exported += '⠀' * shift
                shift += shift_inc
            if len(exported) < max_width:
                if exported and exported[0] in '⠬⠤':
                    prefix = exported[0]
                    exported = exported[1:]
                    if exported and exported[0] == _CONDITIONAL_NUM_PREFIX:
                        exported = exported[1:]
                else:
                    prefix = ''
                if msline_present:
                    prefix = '⠀' + prefix
                formatted = ''
                for i in range(len(pattern) - 1, -1, -1):
                    pos = exported.rfind(pattern[i])
                    part = exported[pos + 1:]
                    exported = exported[:max(pos, 0)]
                    part = '⠀' * (widths[i + 1] - len(part)) + part
                    formatted = pattern[i] + part + formatted
                formatted = (prefix + '⠀' * (widths[0] - len(exported)) + exported + formatted)
                if msline_present:
                    formatted = '⠀' * max(max_width - 1 - len(formatted), 0) + formatted
            else:
                formatted = exported
            result = result + formatted.rstrip('⠀') + '\n'
            if variables.get('msline-present'):
                widths = widths_2
    result = _Braille(result)
    return result
示例#17
0
文件: nemeth.py 项目: dusek/lcg
def _export_mphantom(node, exporter, context, variables, **kwargs):
    braille = _child_export(node, exporter, context, variables)
    n = len(braille)
    return _Braille('⠀' * n)
示例#18
0
文件: nemeth.py 项目: dusek/lcg
def _modifier(variables):
    modifier = _Braille('⠐')
    subsup = variables.get('subsup')
    if subsup:
        modifier = _Braille(_INNER_SUBSUP) + subsup + modifier
    return modifier
示例#19
0
文件: nemeth.py 项目: dusek/lcg
def mathml_nemeth(exporter, context, element):
    class EntityHandler(element.EntityHandler):
        def __init__(self, *args, **kwargs):
            super(EntityHandler, self).__init__(*args, **kwargs)
            self._data = mathml.entities
        def __getitem__(self, key):
            return self._data.get(key, '?')
    entity_handler = EntityHandler()
    top_node = element.tree_content(entity_handler, transform=True)
    post = element.next_element()
    variables = _Variables()
    braille = _child_export(top_node, exporter, context, variables)
    text = braille.text()
    hyphenation = braille.hyphenation().replace(exporter.HYPH_WS, exporter.HYPH_NEMETH_WS)
    # Separate vertical bars
    while True:
        pos = text.find('%s%s' % (_AFTER_BAR, _BEFORE_BAR,))
        if pos == -1:
            break
        text = text[:pos] + '⠐' + text[pos + 2:]
        hyphenation = hyphenation[:pos] + '0' + hyphenation[pos + 2:]
    # Remove repeated subsup's
    while True:
        match = _braille_repeated_subsup_regexp.search(text)
        if match is None:
            break
        start, end = match.span(1)
        text = text[:start] + text[end:]
        hyphenation = hyphenation[:start] + hyphenation[end:]
    # Handle numeric prefixes
    while True:
        match = _num_prefix_regexp.search(text)
        if match is None:
            break
        start, end = match.start(3), match.end(3)
        text = text[:start] + '⠼' + text[end:]
        hyphenation = hyphenation[:start] + '0' + hyphenation[end:]
    # Punctuation indicator -- part 1
    single_letter = text and text[-1] == _SINGLE_LETTER_END
    # Handle letter prefixes
    space_or_punctuation = '⠀⠨⠠⠰'
    while True:
        pos = text.find(_SINGLE_LETTER_START)
        if pos == -1:
            break
        pos_end = text.find(_SINGLE_LETTER_END)
        assert pos_end > pos, text
        # I don't understand Nemeth definition of "single letters" very well.
        # The definitions seem to contradict the examples, especially as for
        # parentheses.  As we use the examples in tests, we try to be
        # consistent with them.  The most important "clarification" rules we
        # add here are:
        # - Single letters may occur at the beginning or at the end of the
        #   whole math construct.
        # - Single letters may be preceeded or succeeded by parentheses, but
        #   not from both the sides -- this is explicitly prohibited by Nemeth,
        #   see §25.a.v-vi, while being explicitly applied in the examples,
        #   see §26.b.(4)-(5).
        pre_punctuation = '' if pos == 0 else text[pos - 1]
        post_punctuation = '' if pos_end >= len(text) - 1 else text[pos_end + 1]
        if ((pre_punctuation == '' or
             pre_punctuation in space_or_punctuation or pre_punctuation == '⠷') and
            (post_punctuation == '' or
             post_punctuation in space_or_punctuation or post_punctuation == '⠾')):
            prefix = '⠰'
            prefix_hyph = '0'
        else:
            prefix = prefix_hyph = ''
        text = text[:pos] + prefix + text[pos + 1:pos_end] + text[pos_end + 1:]
        hyphenation = (hyphenation[:pos] + prefix_hyph + hyphenation[pos + 1:pos_end] +
                       hyphenation[pos_end + 1:])
    # Multipurpose indicator to distinguish base line numbers from subscripts
    while True:
        pos = text.find(_CONDITIONAL_NUM_PREFIX)
        if pos == -1:
            break
        if _braille_separate_subscript_regexp.search(text[:pos]):
            text = text[:pos] + '⠐' + text[pos + 1:]
            hyphenation = hyphenation[:pos] + '0' + hyphenation[pos + 1:]
        else:
            text = text[:pos] + text[pos + 1:]
            hyphenation = hyphenation[:pos] + hyphenation[pos + 1:]
    # Cleanup
    text_len = len(text)
    i = 0
    while i < text_len:
        if text[i] in (_CONDITIONAL_NUM_PREFIX, _NUM_PREFIX_REQUIRED, _IMPLICIT_SUBSCRIPT,
                       _SINGLE_LETTER_KILLER_PREFIX, _SINGLE_LETTER_KILLER_SUFFIX, _INNER_SUBSUP,
                       _AFTER_BAR, _BEFORE_BAR,):
            text = text[:i] + text[i + 1:]
            hyphenation = hyphenation[:i] + hyphenation[i + 1:]
            text_len -= 1
        else:
            i += 1
    # Punctuation indicator -- part 2
    if isinstance(post, lcg.TextContent):
        post_text = post.text()
        punctuated = False
        if post_text:
            if post_text[0] in _prefixed_punctuation:
                punctuated = True
            else:
                m = _punctuation_regexp.match(post_text)
                if m is not None:
                    pos = m.end(1)
                    context.set_alternate_text(post, post_text[:pos] + u'_' + post_text[pos:])
        if punctuated:
            indicate = single_letter
            for indicator in _braille_right_indicators:
                if text.endswith(indicator):
                    indicate = True
                    break
            if not indicate and _braille_number_regexp.search(text):
                indicate = True
            if not indicate:
                last_element = top_node
                children = last_children = last_element.getchildren()
                while last_element.tag not in ('mi', 'mo', 'mn',) and children:
                    last_children = children
                    last_element = children[-1]
                    children = last_element.getchildren()
                if last_element.tag == 'mo' and (last_element.text or '') in ',-–—':
                    if len(last_children) > 1:
                        last_element = last_children[-2]
                if ((last_element.tag == 'mo' or
                     (last_element.tag == 'mi' and last_element.text == '…'))):
                    op = last_element.text.strip()
                    if op in ('–—…' + _signs_of_shape + _math_comparison_operators):
                        indicate = True
                if ((not indicate and last_element.tag == 'mi' and
                     last_element.text.strip() in _function_names)):
                    indicate = True
            if indicate:
                text += '⠸'
                hyphenation += '0'
    # Subscript/superscript base level
    while True:
        pos = text.find(_END_SUBSUP)
        if pos == -1:
            break
        indicate = True
        if pos >= len(text) - 1:
            indicate = False
        elif text[pos + 1] in '⠸⠠%s' % (_END_SUBSUP,):
            indicate = False
        else:
            p = pos + 1
            text_len = len(text)
            while p < text_len and text[p] in '\n⠀':
                p += 1
            if p > pos + 1:
                next_text = text[p:]
                for o in _braille_comparison:
                    if next_text.startswith(o.strip('⠀')):
                        indicate = False
                        break
        pos0 = pos
        while pos0 > 0 and text[pos0 - 1] in '⠰⠘':
            pos0 -= 1
        if indicate:
            text = text[:pos0] + '⠐' + text[pos + 1:]
            hyphenation = hyphenation[:pos0] + '0' + hyphenation[pos + 1:]
        else:
            text = text[:pos0] + text[pos + 1:]
            hyphenation = hyphenation[:pos0] + hyphenation[pos + 1:]
    # Whitespace
    text_len = len(text)
    i = 0
    while i < text_len:
        space = True
        if text[i] == _LEFT_WHITESPACE_42:
            t = text[:i]
            if not post and _braille_empty_regexp.match(t):
                space = False
            elif not _braille_number_regexp.search(t): # numbers may look like punctuation
                for b in (_braille_punctuation + _braille_right_indicators +
                          _braille_left_grouping + _braille_right_grouping + _braille_symbols_42 +
                          ('⠀',)):
                    if b != '⠤' and t.endswith(b):
                        space = False
                        break
        elif text[i] == _RIGHT_WHITESPACE_42:
            t = text[i + 1:]
            if not post and _braille_empty_regexp.match(t):
                space = False
            else:
                for b in (_braille_punctuation + _braille_left_indicators +
                          _braille_left_grouping + _braille_right_grouping + _braille_symbols_42 +
                          ('⠀',)):
                    if b != '⠤' and b != '⠼' and t.startswith(b):
                        # We do insert space before numeric indicator.  It
                        # seems to contradict §42-43 but it respects the
                        # example in §9 (we prefer the examples in case of
                        # conflicts).
                        space = False
                        break
        else:
            i += 1
            continue
        if space:
            text = text[:i] + '⠀' + text[i + 1:]
            hyphenation = hyphenation[:i] + '4' + hyphenation[i + 1:]
            i += 1
        else:
            text = text[:i] + text[i + 1:]
            hyphenation = hyphenation[:i] + hyphenation[i + 1:]
            text_len -= 1
    # Adjust matrices
    while True:
        start = text.find(_MATRIX_START)
        if start == -1:
            break
        end = text.find(_MATRIX_END)
        assert end > start + 1
        rows = text[start + 1:end - 1].split('\n')
        n_columns = len(rows[0].split(_MATRIX_SEPARATOR))
        column_widths = [0] * n_columns
        for r in rows:
            column_widths = [max(w, len(c))
                             for w, c in zip(column_widths, r.split(_MATRIX_SEPARATOR))]
        matrix = ''
        for r in rows:
            cells = r.split(_MATRIX_SEPARATOR)
            for i in range(n_columns):
                c = cells[i]
                matrix += c + '⠀' * (column_widths[i] - len(c) + (1 if i < n_columns - 2 else 0))
            matrix += '\n'
        text = text[:start] + matrix + text[end + 1:]
        hyphenation = hyphenation[:start] + '0' * len(matrix) + hyphenation[end + 1:]
    # Done
    return _Braille(text, hyphenation)