Created
September 4, 2018 18:31
-
-
Save sidawson/a5c24d7e0b89004d134d9425e2c04082 to your computer and use it in GitHub Desktop.
align non-proportional unicode font (Menlo, 12pt)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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