644 lines
20 KiB
Python
644 lines
20 KiB
Python
"""Symbolic primitives + unicode/ASCII abstraction for pretty.py"""
|
|
|
|
import sys
|
|
import warnings
|
|
from string import ascii_lowercase, ascii_uppercase
|
|
import unicodedata
|
|
|
|
unicode_warnings = ''
|
|
|
|
def U(name):
|
|
"""
|
|
Get a unicode character by name or, None if not found.
|
|
|
|
This exists because older versions of Python use older unicode databases.
|
|
"""
|
|
try:
|
|
return unicodedata.lookup(name)
|
|
except KeyError:
|
|
global unicode_warnings
|
|
unicode_warnings += 'No \'%s\' in unicodedata\n' % name
|
|
return None
|
|
|
|
from sympy.printing.conventions import split_super_sub
|
|
from sympy.core.alphabets import greeks
|
|
from sympy.utilities.exceptions import sympy_deprecation_warning
|
|
|
|
# prefix conventions when constructing tables
|
|
# L - LATIN i
|
|
# G - GREEK beta
|
|
# D - DIGIT 0
|
|
# S - SYMBOL +
|
|
|
|
|
|
__all__ = ['greek_unicode', 'sub', 'sup', 'xsym', 'vobj', 'hobj', 'pretty_symbol',
|
|
'annotated']
|
|
|
|
|
|
_use_unicode = False
|
|
|
|
|
|
def pretty_use_unicode(flag=None):
|
|
"""Set whether pretty-printer should use unicode by default"""
|
|
global _use_unicode
|
|
global unicode_warnings
|
|
if flag is None:
|
|
return _use_unicode
|
|
|
|
if flag and unicode_warnings:
|
|
# print warnings (if any) on first unicode usage
|
|
warnings.warn(unicode_warnings)
|
|
unicode_warnings = ''
|
|
|
|
use_unicode_prev = _use_unicode
|
|
_use_unicode = flag
|
|
return use_unicode_prev
|
|
|
|
|
|
def pretty_try_use_unicode():
|
|
"""See if unicode output is available and leverage it if possible"""
|
|
|
|
encoding = getattr(sys.stdout, 'encoding', None)
|
|
|
|
# this happens when e.g. stdout is redirected through a pipe, or is
|
|
# e.g. a cStringIO.StringO
|
|
if encoding is None:
|
|
return # sys.stdout has no encoding
|
|
|
|
symbols = []
|
|
|
|
# see if we can represent greek alphabet
|
|
symbols += greek_unicode.values()
|
|
|
|
# and atoms
|
|
symbols += atoms_table.values()
|
|
|
|
for s in symbols:
|
|
if s is None:
|
|
return # common symbols not present!
|
|
|
|
try:
|
|
s.encode(encoding)
|
|
except UnicodeEncodeError:
|
|
return
|
|
|
|
# all the characters were present and encodable
|
|
pretty_use_unicode(True)
|
|
|
|
|
|
def xstr(*args):
|
|
sympy_deprecation_warning(
|
|
"""
|
|
The sympy.printing.pretty.pretty_symbology.xstr() function is
|
|
deprecated. Use str() instead.
|
|
""",
|
|
deprecated_since_version="1.7",
|
|
active_deprecations_target="deprecated-pretty-printing-functions"
|
|
)
|
|
return str(*args)
|
|
|
|
# GREEK
|
|
g = lambda l: U('GREEK SMALL LETTER %s' % l.upper())
|
|
G = lambda l: U('GREEK CAPITAL LETTER %s' % l.upper())
|
|
|
|
greek_letters = list(greeks) # make a copy
|
|
# deal with Unicode's funny spelling of lambda
|
|
greek_letters[greek_letters.index('lambda')] = 'lamda'
|
|
|
|
# {} greek letter -> (g,G)
|
|
greek_unicode = {L: g(L) for L in greek_letters}
|
|
greek_unicode.update((L[0].upper() + L[1:], G(L)) for L in greek_letters)
|
|
|
|
# aliases
|
|
greek_unicode['lambda'] = greek_unicode['lamda']
|
|
greek_unicode['Lambda'] = greek_unicode['Lamda']
|
|
greek_unicode['varsigma'] = '\N{GREEK SMALL LETTER FINAL SIGMA}'
|
|
|
|
# BOLD
|
|
b = lambda l: U('MATHEMATICAL BOLD SMALL %s' % l.upper())
|
|
B = lambda l: U('MATHEMATICAL BOLD CAPITAL %s' % l.upper())
|
|
|
|
bold_unicode = {l: b(l) for l in ascii_lowercase}
|
|
bold_unicode.update((L, B(L)) for L in ascii_uppercase)
|
|
|
|
# GREEK BOLD
|
|
gb = lambda l: U('MATHEMATICAL BOLD SMALL %s' % l.upper())
|
|
GB = lambda l: U('MATHEMATICAL BOLD CAPITAL %s' % l.upper())
|
|
|
|
greek_bold_letters = list(greeks) # make a copy, not strictly required here
|
|
# deal with Unicode's funny spelling of lambda
|
|
greek_bold_letters[greek_bold_letters.index('lambda')] = 'lamda'
|
|
|
|
# {} greek letter -> (g,G)
|
|
greek_bold_unicode = {L: g(L) for L in greek_bold_letters}
|
|
greek_bold_unicode.update((L[0].upper() + L[1:], G(L)) for L in greek_bold_letters)
|
|
greek_bold_unicode['lambda'] = greek_unicode['lamda']
|
|
greek_bold_unicode['Lambda'] = greek_unicode['Lamda']
|
|
greek_bold_unicode['varsigma'] = '\N{MATHEMATICAL BOLD SMALL FINAL SIGMA}'
|
|
|
|
digit_2txt = {
|
|
'0': 'ZERO',
|
|
'1': 'ONE',
|
|
'2': 'TWO',
|
|
'3': 'THREE',
|
|
'4': 'FOUR',
|
|
'5': 'FIVE',
|
|
'6': 'SIX',
|
|
'7': 'SEVEN',
|
|
'8': 'EIGHT',
|
|
'9': 'NINE',
|
|
}
|
|
|
|
symb_2txt = {
|
|
'+': 'PLUS SIGN',
|
|
'-': 'MINUS',
|
|
'=': 'EQUALS SIGN',
|
|
'(': 'LEFT PARENTHESIS',
|
|
')': 'RIGHT PARENTHESIS',
|
|
'[': 'LEFT SQUARE BRACKET',
|
|
']': 'RIGHT SQUARE BRACKET',
|
|
'{': 'LEFT CURLY BRACKET',
|
|
'}': 'RIGHT CURLY BRACKET',
|
|
|
|
# non-std
|
|
'{}': 'CURLY BRACKET',
|
|
'sum': 'SUMMATION',
|
|
'int': 'INTEGRAL',
|
|
}
|
|
|
|
# SUBSCRIPT & SUPERSCRIPT
|
|
LSUB = lambda letter: U('LATIN SUBSCRIPT SMALL LETTER %s' % letter.upper())
|
|
GSUB = lambda letter: U('GREEK SUBSCRIPT SMALL LETTER %s' % letter.upper())
|
|
DSUB = lambda digit: U('SUBSCRIPT %s' % digit_2txt[digit])
|
|
SSUB = lambda symb: U('SUBSCRIPT %s' % symb_2txt[symb])
|
|
|
|
LSUP = lambda letter: U('SUPERSCRIPT LATIN SMALL LETTER %s' % letter.upper())
|
|
DSUP = lambda digit: U('SUPERSCRIPT %s' % digit_2txt[digit])
|
|
SSUP = lambda symb: U('SUPERSCRIPT %s' % symb_2txt[symb])
|
|
|
|
sub = {} # symb -> subscript symbol
|
|
sup = {} # symb -> superscript symbol
|
|
|
|
# latin subscripts
|
|
for l in 'aeioruvxhklmnpst':
|
|
sub[l] = LSUB(l)
|
|
|
|
for l in 'in':
|
|
sup[l] = LSUP(l)
|
|
|
|
for gl in ['beta', 'gamma', 'rho', 'phi', 'chi']:
|
|
sub[gl] = GSUB(gl)
|
|
|
|
for d in [str(i) for i in range(10)]:
|
|
sub[d] = DSUB(d)
|
|
sup[d] = DSUP(d)
|
|
|
|
for s in '+-=()':
|
|
sub[s] = SSUB(s)
|
|
sup[s] = SSUP(s)
|
|
|
|
# Variable modifiers
|
|
# TODO: Make brackets adjust to height of contents
|
|
modifier_dict = {
|
|
# Accents
|
|
'mathring': lambda s: center_accent(s, '\N{COMBINING RING ABOVE}'),
|
|
'ddddot': lambda s: center_accent(s, '\N{COMBINING FOUR DOTS ABOVE}'),
|
|
'dddot': lambda s: center_accent(s, '\N{COMBINING THREE DOTS ABOVE}'),
|
|
'ddot': lambda s: center_accent(s, '\N{COMBINING DIAERESIS}'),
|
|
'dot': lambda s: center_accent(s, '\N{COMBINING DOT ABOVE}'),
|
|
'check': lambda s: center_accent(s, '\N{COMBINING CARON}'),
|
|
'breve': lambda s: center_accent(s, '\N{COMBINING BREVE}'),
|
|
'acute': lambda s: center_accent(s, '\N{COMBINING ACUTE ACCENT}'),
|
|
'grave': lambda s: center_accent(s, '\N{COMBINING GRAVE ACCENT}'),
|
|
'tilde': lambda s: center_accent(s, '\N{COMBINING TILDE}'),
|
|
'hat': lambda s: center_accent(s, '\N{COMBINING CIRCUMFLEX ACCENT}'),
|
|
'bar': lambda s: center_accent(s, '\N{COMBINING OVERLINE}'),
|
|
'vec': lambda s: center_accent(s, '\N{COMBINING RIGHT ARROW ABOVE}'),
|
|
'prime': lambda s: s+'\N{PRIME}',
|
|
'prm': lambda s: s+'\N{PRIME}',
|
|
# # Faces -- these are here for some compatibility with latex printing
|
|
# 'bold': lambda s: s,
|
|
# 'bm': lambda s: s,
|
|
# 'cal': lambda s: s,
|
|
# 'scr': lambda s: s,
|
|
# 'frak': lambda s: s,
|
|
# Brackets
|
|
'norm': lambda s: '\N{DOUBLE VERTICAL LINE}'+s+'\N{DOUBLE VERTICAL LINE}',
|
|
'avg': lambda s: '\N{MATHEMATICAL LEFT ANGLE BRACKET}'+s+'\N{MATHEMATICAL RIGHT ANGLE BRACKET}',
|
|
'abs': lambda s: '\N{VERTICAL LINE}'+s+'\N{VERTICAL LINE}',
|
|
'mag': lambda s: '\N{VERTICAL LINE}'+s+'\N{VERTICAL LINE}',
|
|
}
|
|
|
|
# VERTICAL OBJECTS
|
|
HUP = lambda symb: U('%s UPPER HOOK' % symb_2txt[symb])
|
|
CUP = lambda symb: U('%s UPPER CORNER' % symb_2txt[symb])
|
|
MID = lambda symb: U('%s MIDDLE PIECE' % symb_2txt[symb])
|
|
EXT = lambda symb: U('%s EXTENSION' % symb_2txt[symb])
|
|
HLO = lambda symb: U('%s LOWER HOOK' % symb_2txt[symb])
|
|
CLO = lambda symb: U('%s LOWER CORNER' % symb_2txt[symb])
|
|
TOP = lambda symb: U('%s TOP' % symb_2txt[symb])
|
|
BOT = lambda symb: U('%s BOTTOM' % symb_2txt[symb])
|
|
|
|
# {} '(' -> (extension, start, end, middle) 1-character
|
|
_xobj_unicode = {
|
|
|
|
# vertical symbols
|
|
# (( ext, top, bot, mid ), c1)
|
|
'(': (( EXT('('), HUP('('), HLO('(') ), '('),
|
|
')': (( EXT(')'), HUP(')'), HLO(')') ), ')'),
|
|
'[': (( EXT('['), CUP('['), CLO('[') ), '['),
|
|
']': (( EXT(']'), CUP(']'), CLO(']') ), ']'),
|
|
'{': (( EXT('{}'), HUP('{'), HLO('{'), MID('{') ), '{'),
|
|
'}': (( EXT('{}'), HUP('}'), HLO('}'), MID('}') ), '}'),
|
|
'|': U('BOX DRAWINGS LIGHT VERTICAL'),
|
|
|
|
'<': ((U('BOX DRAWINGS LIGHT VERTICAL'),
|
|
U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
|
|
U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT')), '<'),
|
|
|
|
'>': ((U('BOX DRAWINGS LIGHT VERTICAL'),
|
|
U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
|
|
U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), '>'),
|
|
|
|
'lfloor': (( EXT('['), EXT('['), CLO('[') ), U('LEFT FLOOR')),
|
|
'rfloor': (( EXT(']'), EXT(']'), CLO(']') ), U('RIGHT FLOOR')),
|
|
'lceil': (( EXT('['), CUP('['), EXT('[') ), U('LEFT CEILING')),
|
|
'rceil': (( EXT(']'), CUP(']'), EXT(']') ), U('RIGHT CEILING')),
|
|
|
|
'int': (( EXT('int'), U('TOP HALF INTEGRAL'), U('BOTTOM HALF INTEGRAL') ), U('INTEGRAL')),
|
|
'sum': (( U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), '_', U('OVERLINE'), U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), U('N-ARY SUMMATION')),
|
|
|
|
# horizontal objects
|
|
#'-': '-',
|
|
'-': U('BOX DRAWINGS LIGHT HORIZONTAL'),
|
|
'_': U('LOW LINE'),
|
|
# We used to use this, but LOW LINE looks better for roots, as it's a
|
|
# little lower (i.e., it lines up with the / perfectly. But perhaps this
|
|
# one would still be wanted for some cases?
|
|
# '_': U('HORIZONTAL SCAN LINE-9'),
|
|
|
|
# diagonal objects '\' & '/' ?
|
|
'/': U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
|
|
'\\': U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
|
|
}
|
|
|
|
_xobj_ascii = {
|
|
# vertical symbols
|
|
# (( ext, top, bot, mid ), c1)
|
|
'(': (( '|', '/', '\\' ), '('),
|
|
')': (( '|', '\\', '/' ), ')'),
|
|
|
|
# XXX this looks ugly
|
|
# '[': (( '|', '-', '-' ), '['),
|
|
# ']': (( '|', '-', '-' ), ']'),
|
|
# XXX not so ugly :(
|
|
'[': (( '[', '[', '[' ), '['),
|
|
']': (( ']', ']', ']' ), ']'),
|
|
|
|
'{': (( '|', '/', '\\', '<' ), '{'),
|
|
'}': (( '|', '\\', '/', '>' ), '}'),
|
|
'|': '|',
|
|
|
|
'<': (( '|', '/', '\\' ), '<'),
|
|
'>': (( '|', '\\', '/' ), '>'),
|
|
|
|
'int': ( ' | ', ' /', '/ ' ),
|
|
|
|
# horizontal objects
|
|
'-': '-',
|
|
'_': '_',
|
|
|
|
# diagonal objects '\' & '/' ?
|
|
'/': '/',
|
|
'\\': '\\',
|
|
}
|
|
|
|
|
|
def xobj(symb, length):
|
|
"""Construct spatial object of given length.
|
|
|
|
return: [] of equal-length strings
|
|
"""
|
|
|
|
if length <= 0:
|
|
raise ValueError("Length should be greater than 0")
|
|
|
|
# TODO robustify when no unicodedat available
|
|
if _use_unicode:
|
|
_xobj = _xobj_unicode
|
|
else:
|
|
_xobj = _xobj_ascii
|
|
|
|
vinfo = _xobj[symb]
|
|
|
|
c1 = top = bot = mid = None
|
|
|
|
if not isinstance(vinfo, tuple): # 1 entry
|
|
ext = vinfo
|
|
else:
|
|
if isinstance(vinfo[0], tuple): # (vlong), c1
|
|
vlong = vinfo[0]
|
|
c1 = vinfo[1]
|
|
else: # (vlong), c1
|
|
vlong = vinfo
|
|
|
|
ext = vlong[0]
|
|
|
|
try:
|
|
top = vlong[1]
|
|
bot = vlong[2]
|
|
mid = vlong[3]
|
|
except IndexError:
|
|
pass
|
|
|
|
if c1 is None:
|
|
c1 = ext
|
|
if top is None:
|
|
top = ext
|
|
if bot is None:
|
|
bot = ext
|
|
if mid is not None:
|
|
if (length % 2) == 0:
|
|
# even height, but we have to print it somehow anyway...
|
|
# XXX is it ok?
|
|
length += 1
|
|
|
|
else:
|
|
mid = ext
|
|
|
|
if length == 1:
|
|
return c1
|
|
|
|
res = []
|
|
next = (length - 2)//2
|
|
nmid = (length - 2) - next*2
|
|
|
|
res += [top]
|
|
res += [ext]*next
|
|
res += [mid]*nmid
|
|
res += [ext]*next
|
|
res += [bot]
|
|
|
|
return res
|
|
|
|
|
|
def vobj(symb, height):
|
|
"""Construct vertical object of a given height
|
|
|
|
see: xobj
|
|
"""
|
|
return '\n'.join( xobj(symb, height) )
|
|
|
|
|
|
def hobj(symb, width):
|
|
"""Construct horizontal object of a given width
|
|
|
|
see: xobj
|
|
"""
|
|
return ''.join( xobj(symb, width) )
|
|
|
|
# RADICAL
|
|
# n -> symbol
|
|
root = {
|
|
2: U('SQUARE ROOT'), # U('RADICAL SYMBOL BOTTOM')
|
|
3: U('CUBE ROOT'),
|
|
4: U('FOURTH ROOT'),
|
|
}
|
|
|
|
|
|
# RATIONAL
|
|
VF = lambda txt: U('VULGAR FRACTION %s' % txt)
|
|
|
|
# (p,q) -> symbol
|
|
frac = {
|
|
(1, 2): VF('ONE HALF'),
|
|
(1, 3): VF('ONE THIRD'),
|
|
(2, 3): VF('TWO THIRDS'),
|
|
(1, 4): VF('ONE QUARTER'),
|
|
(3, 4): VF('THREE QUARTERS'),
|
|
(1, 5): VF('ONE FIFTH'),
|
|
(2, 5): VF('TWO FIFTHS'),
|
|
(3, 5): VF('THREE FIFTHS'),
|
|
(4, 5): VF('FOUR FIFTHS'),
|
|
(1, 6): VF('ONE SIXTH'),
|
|
(5, 6): VF('FIVE SIXTHS'),
|
|
(1, 8): VF('ONE EIGHTH'),
|
|
(3, 8): VF('THREE EIGHTHS'),
|
|
(5, 8): VF('FIVE EIGHTHS'),
|
|
(7, 8): VF('SEVEN EIGHTHS'),
|
|
}
|
|
|
|
|
|
# atom symbols
|
|
_xsym = {
|
|
'==': ('=', '='),
|
|
'<': ('<', '<'),
|
|
'>': ('>', '>'),
|
|
'<=': ('<=', U('LESS-THAN OR EQUAL TO')),
|
|
'>=': ('>=', U('GREATER-THAN OR EQUAL TO')),
|
|
'!=': ('!=', U('NOT EQUAL TO')),
|
|
':=': (':=', ':='),
|
|
'+=': ('+=', '+='),
|
|
'-=': ('-=', '-='),
|
|
'*=': ('*=', '*='),
|
|
'/=': ('/=', '/='),
|
|
'%=': ('%=', '%='),
|
|
'*': ('*', U('DOT OPERATOR')),
|
|
'-->': ('-->', U('EM DASH') + U('EM DASH') +
|
|
U('BLACK RIGHT-POINTING TRIANGLE') if U('EM DASH')
|
|
and U('BLACK RIGHT-POINTING TRIANGLE') else None),
|
|
'==>': ('==>', U('BOX DRAWINGS DOUBLE HORIZONTAL') +
|
|
U('BOX DRAWINGS DOUBLE HORIZONTAL') +
|
|
U('BLACK RIGHT-POINTING TRIANGLE') if
|
|
U('BOX DRAWINGS DOUBLE HORIZONTAL') and
|
|
U('BOX DRAWINGS DOUBLE HORIZONTAL') and
|
|
U('BLACK RIGHT-POINTING TRIANGLE') else None),
|
|
'.': ('*', U('RING OPERATOR')),
|
|
}
|
|
|
|
|
|
def xsym(sym):
|
|
"""get symbology for a 'character'"""
|
|
op = _xsym[sym]
|
|
|
|
if _use_unicode:
|
|
return op[1]
|
|
else:
|
|
return op[0]
|
|
|
|
|
|
# SYMBOLS
|
|
|
|
atoms_table = {
|
|
# class how-to-display
|
|
'Exp1': U('SCRIPT SMALL E'),
|
|
'Pi': U('GREEK SMALL LETTER PI'),
|
|
'Infinity': U('INFINITY'),
|
|
'NegativeInfinity': U('INFINITY') and ('-' + U('INFINITY')), # XXX what to do here
|
|
#'ImaginaryUnit': U('GREEK SMALL LETTER IOTA'),
|
|
#'ImaginaryUnit': U('MATHEMATICAL ITALIC SMALL I'),
|
|
'ImaginaryUnit': U('DOUBLE-STRUCK ITALIC SMALL I'),
|
|
'EmptySet': U('EMPTY SET'),
|
|
'Naturals': U('DOUBLE-STRUCK CAPITAL N'),
|
|
'Naturals0': (U('DOUBLE-STRUCK CAPITAL N') and
|
|
(U('DOUBLE-STRUCK CAPITAL N') +
|
|
U('SUBSCRIPT ZERO'))),
|
|
'Integers': U('DOUBLE-STRUCK CAPITAL Z'),
|
|
'Rationals': U('DOUBLE-STRUCK CAPITAL Q'),
|
|
'Reals': U('DOUBLE-STRUCK CAPITAL R'),
|
|
'Complexes': U('DOUBLE-STRUCK CAPITAL C'),
|
|
'Union': U('UNION'),
|
|
'SymmetricDifference': U('INCREMENT'),
|
|
'Intersection': U('INTERSECTION'),
|
|
'Ring': U('RING OPERATOR'),
|
|
'Modifier Letter Low Ring':U('Modifier Letter Low Ring'),
|
|
'EmptySequence': 'EmptySequence',
|
|
}
|
|
|
|
|
|
def pretty_atom(atom_name, default=None, printer=None):
|
|
"""return pretty representation of an atom"""
|
|
if _use_unicode:
|
|
if printer is not None and atom_name == 'ImaginaryUnit' and printer._settings['imaginary_unit'] == 'j':
|
|
return U('DOUBLE-STRUCK ITALIC SMALL J')
|
|
else:
|
|
return atoms_table[atom_name]
|
|
else:
|
|
if default is not None:
|
|
return default
|
|
|
|
raise KeyError('only unicode') # send it default printer
|
|
|
|
|
|
def pretty_symbol(symb_name, bold_name=False):
|
|
"""return pretty representation of a symbol"""
|
|
# let's split symb_name into symbol + index
|
|
# UC: beta1
|
|
# UC: f_beta
|
|
|
|
if not _use_unicode:
|
|
return symb_name
|
|
|
|
name, sups, subs = split_super_sub(symb_name)
|
|
|
|
def translate(s, bold_name) :
|
|
if bold_name:
|
|
gG = greek_bold_unicode.get(s)
|
|
else:
|
|
gG = greek_unicode.get(s)
|
|
if gG is not None:
|
|
return gG
|
|
for key in sorted(modifier_dict.keys(), key=lambda k:len(k), reverse=True) :
|
|
if s.lower().endswith(key) and len(s)>len(key):
|
|
return modifier_dict[key](translate(s[:-len(key)], bold_name))
|
|
if bold_name:
|
|
return ''.join([bold_unicode[c] for c in s])
|
|
return s
|
|
|
|
name = translate(name, bold_name)
|
|
|
|
# Let's prettify sups/subs. If it fails at one of them, pretty sups/subs are
|
|
# not used at all.
|
|
def pretty_list(l, mapping):
|
|
result = []
|
|
for s in l:
|
|
pretty = mapping.get(s)
|
|
if pretty is None:
|
|
try: # match by separate characters
|
|
pretty = ''.join([mapping[c] for c in s])
|
|
except (TypeError, KeyError):
|
|
return None
|
|
result.append(pretty)
|
|
return result
|
|
|
|
pretty_sups = pretty_list(sups, sup)
|
|
if pretty_sups is not None:
|
|
pretty_subs = pretty_list(subs, sub)
|
|
else:
|
|
pretty_subs = None
|
|
|
|
# glue the results into one string
|
|
if pretty_subs is None: # nice formatting of sups/subs did not work
|
|
if subs:
|
|
name += '_'+'_'.join([translate(s, bold_name) for s in subs])
|
|
if sups:
|
|
name += '__'+'__'.join([translate(s, bold_name) for s in sups])
|
|
return name
|
|
else:
|
|
sups_result = ' '.join(pretty_sups)
|
|
subs_result = ' '.join(pretty_subs)
|
|
|
|
return ''.join([name, sups_result, subs_result])
|
|
|
|
|
|
def annotated(letter):
|
|
"""
|
|
Return a stylised drawing of the letter ``letter``, together with
|
|
information on how to put annotations (super- and subscripts to the
|
|
left and to the right) on it.
|
|
|
|
See pretty.py functions _print_meijerg, _print_hyper on how to use this
|
|
information.
|
|
"""
|
|
ucode_pics = {
|
|
'F': (2, 0, 2, 0, '\N{BOX DRAWINGS LIGHT DOWN AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\n'
|
|
'\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\n'
|
|
'\N{BOX DRAWINGS LIGHT UP}'),
|
|
'G': (3, 0, 3, 1, '\N{BOX DRAWINGS LIGHT ARC DOWN AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{BOX DRAWINGS LIGHT ARC DOWN AND LEFT}\n'
|
|
'\N{BOX DRAWINGS LIGHT VERTICAL}\N{BOX DRAWINGS LIGHT RIGHT}\N{BOX DRAWINGS LIGHT DOWN AND LEFT}\n'
|
|
'\N{BOX DRAWINGS LIGHT ARC UP AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{BOX DRAWINGS LIGHT ARC UP AND LEFT}')
|
|
}
|
|
ascii_pics = {
|
|
'F': (3, 0, 3, 0, ' _\n|_\n|\n'),
|
|
'G': (3, 0, 3, 1, ' __\n/__\n\\_|')
|
|
}
|
|
|
|
if _use_unicode:
|
|
return ucode_pics[letter]
|
|
else:
|
|
return ascii_pics[letter]
|
|
|
|
_remove_combining = dict.fromkeys(list(range(ord('\N{COMBINING GRAVE ACCENT}'), ord('\N{COMBINING LATIN SMALL LETTER X}')))
|
|
+ list(range(ord('\N{COMBINING LEFT HARPOON ABOVE}'), ord('\N{COMBINING ASTERISK ABOVE}'))))
|
|
|
|
def is_combining(sym):
|
|
"""Check whether symbol is a unicode modifier. """
|
|
|
|
return ord(sym) in _remove_combining
|
|
|
|
|
|
def center_accent(string, accent):
|
|
"""
|
|
Returns a string with accent inserted on the middle character. Useful to
|
|
put combining accents on symbol names, including multi-character names.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
string : string
|
|
The string to place the accent in.
|
|
accent : string
|
|
The combining accent to insert
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Combining_character
|
|
.. [2] https://en.wikipedia.org/wiki/Combining_Diacritical_Marks
|
|
|
|
"""
|
|
|
|
# Accent is placed on the previous character, although it may not always look
|
|
# like that depending on console
|
|
midpoint = len(string) // 2 + 1
|
|
firstpart = string[:midpoint]
|
|
secondpart = string[midpoint:]
|
|
return firstpart + accent + secondpart
|
|
|
|
|
|
def line_width(line):
|
|
"""Unicode combining symbols (modifiers) are not ever displayed as
|
|
separate symbols and thus should not be counted
|
|
"""
|
|
return len(line.translate(_remove_combining))
|