Skip to content

Instantly share code, notes, and snippets.

@sidawson
Created September 4, 2018 18:31
Show Gist options
  • Save sidawson/a5c24d7e0b89004d134d9425e2c04082 to your computer and use it in GitHub Desktop.
Save sidawson/a5c24d7e0b89004d134d9425e2c04082 to your computer and use it in GitHub Desktop.
align non-proportional unicode font (Menlo, 12pt)
# Apologies if the indents are still a bit fucked.
# Got 2/3 through before I noticed the "spaces, 2" option top right.
# Why default to 2? Madness
def fancy_font(in_str, is_bold=False, is_italic=False):
''' makes fonts fancy! Bold+italic WILL fuck up visual length
only works with a-zA-Z0-9. but, holy fuck, it works at all.
why? COZ WE CAN OUTPUT INTO A PLAIN UTF-8 TEXTBOX WITH NO COLOUR!
(Sublime Text 3 output panel, I'm looking at you)
'''
ord_add_lower = ord_add_upper = ord_add_number = 0
# bold. lower = +120205 upper = +120211
if is_bold:
ord_add_lower = 120205
ord_add_upper = 120211
# yay, we can do bold numbers!
ord_add_number = 120764
# italic. lower = +120257 upper = +120263
if is_italic:
ord_add_lower = 120257
ord_add_upper = 120263
# bold italic. lower = +120309 upper = +120315
if is_bold and is_italic:
ord_add_lower = 120309
ord_add_upper = 120315
# yes yes, I'm sure there are better ways of doing this. it's good enough; let's move on
alpha_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
numbers = '0123456789'
output_str = []
for char in in_str:
if char in alpha_chars:
if char.lower() == char:
output_str.append(ord(char) + ord_add_lower)
else:
output_str.append(ord(char) + ord_add_upper)
elif char in numbers:
output_str.append(ord(char) + ord_add_number)
else:
output_str.append(ord(char))
return ''.join((chr(char) for char in output_str))
def manually_figure_out_scales():
''' tweak these magic numbers (yes yes, I know) until the vertical bars align as closely as possible
obviously you only need to do this once per font/size, BUT, then c&p these figures into get_fancy_char_width.
for clarity/simplicity, I've removed all the '1's.
'''
# note: m & w won't work with this technique. Just eyeball them for now
scales = {'a': .87, 'c': .75, 'e': .87, 'f': .62, 'g': .87, 'i': .5, 'j': .5, 'k': .87, 'l': .5, 'm': 1.4,
'o': .87, 'r': .62, 's': .75, 't': .62, 'v': .87, 'w': 1.4, 'x': .87, 'y': .87, 'z': .75}
# the full list
# for item in ("abcdefghijklmnopqrstuvwxyz"):
# the specific ones you're working on - so less scrolling
for char in ("bacfimwz"):
bold_char = fancy_font(char, is_bold=True)
# note - get the scale on the ASCII part of the text, NOT the unicode (otherwise won't match the dict)
scale = scales.get(char, 1)
print("|{}{}|".format(bold_char * 100, ' '* int((100-100*scale))))
def get_fancy_char_width(char, is_bold=False):
''' gets the relative visual width of bold Menlo 12 characters
since Menlo is (normally, lol) non-proportional, a scale of 1 = normal (non bold) char width
'''
if not char:
return 0
if not is_bold:
return 1
bold_scales = {'a': .87, 'c': .75, 'e': .87, 'f': .62, 'g': .87, 'i': .5, 'j': .5, 'k': .87, 'l': .5, 'm': 1.5,
'o': .87, 'r': .62, 's': .75, 't': .62, 'v': .87, 'w': 1.5, 'x': .87, 'y': .87, 'z': .75}
scale = bold_scales.get(char, 1)
return scale
def calc_closest_space_combo(str_length, length):
''' calcs the closest combination of weirdo spaces to match the exact length
given that the text filling said length is of (float) str_length
returns (a, b, c)
where:
a - number of regular spaces
b - number of braille blanks
c - number of ideographic spaces
sadly, Menlo has very limited number of "non-regular-width blank spaces" to work with
'''
# number of 'normal char' widths left to fill
left = length - str_length
# simple case, no bold chars
if str_length == int(str_length):
return int(left), 0, 0
# widths of:
# regular space
a = 1
# braille blank
b = 1/0.89
# ideographic space
c = 1/0.617
max_delta = 0.1
best_b = best_c = -1
best_total = best_delta = -1
# scan possibilities
# yep, for efficiency, this should be in a pre-calced matrix. bite me.
# yep, there's also probably some clever clogs non-brute force method possible (but less code=fewer bugs)
# efficiency not needed for my use case. These are left as an exercise for the reader
for this_b, this_c in zip(range(0,9), range(0,9)):
# avoid the stupid case
if this_b == this_c == 0:
continue
# calc the total length of string + this combination of spaces
this_total = str_length + this_b * b + this_c * c
# exit if we've gone too far (sadly, backspaces won't help)
if this_total > length:
break
# calc fraction of a char width
this_fraction = this_total - int(this_total)
# tweak it so we get the closest absolute fractional distance (eg 0.89 & 0.11 are both equally good)
this_delta = 0.5 + abs(this_fraction - 0.5)
# track best combo.
# note, we basically ignore regular sized spaces, since we can chuck those in at the end
if this_delta > best_delta:
best_total = this_total
best_delta = this_delta
best_b, best_c = this_b, this_c
# fill the remaining spcae with regular sized spaces
return int(length - round(best_total, 0)), int(best_b), int(best_c)
def print_aligned_text(text, align_width):
''' use this however you like. This is NOT how I use it. lol
although, I do quite like bananas...
'''
# interestingly, due to m's & w's being WIDER than 1 char, a bunch of those in here WILL break this
# but then, how boring would that be to look at anyway? It works pretty well for regular text
fancy_text = fancy_font(text, is_bold=True)[:align_width]
# NOTE: get the char widths based on the ASCII version of the string, not the bolded/unicode version
total_width = sum((get_fancy_char_width(char, is_bold=True) for char in text))
# get whatever random combo of weirdo spaces will make it (<cough>almost<cough>) align
a, b, c = calc_closest_space_combo(total_width, align_width)
# normal space
a_space = ' '
# braille pattern blank
c_space = '⠀'
# ideographic space
b_space = ' '
# note: the bananas should be vertically aligned, despite the crazy wonkiness of the non-proportional bolded Menlo 12pt font
# this is a singing dog of a solution - it's so amazing to see, nobody (ME) cares too much how WELL it sings.
print("{}{}|{}".format(fancy_text, a_space*a, b_space*b, c_space*c, "Ooh! A Banana"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment