504 lines
12 KiB
Python
504 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
|
||
import inkex
|
||
|
||
# ---------------------------------
|
||
|
||
# UTILITIES
|
||
|
||
# Common standards
|
||
|
||
UPPERCASE_PREFIXES = {
|
||
chr(15): 0x2828, # uppercase prefix: https://codepoints.net/U+000F
|
||
}
|
||
|
||
|
||
LOUIS_BRAILLE_NUMBERS_PREFIX = 0x283c # Louis Braille's numbers prefix
|
||
LOUIS_BRAILLE_NUMBERS = { # Louis Braille's original numbers codification
|
||
"0": 0x281a,
|
||
"1": 0x2801,
|
||
"2": 0x2803,
|
||
"3": 0x2809,
|
||
"4": 0x2819,
|
||
"5": 0x2811,
|
||
"6": 0x280B,
|
||
"7": 0x281b,
|
||
"8": 0x2813,
|
||
"9": 0x280a,
|
||
}
|
||
|
||
# ---------------------
|
||
|
||
# English based locales
|
||
|
||
EN_ASCII = " A1B'K2L@CIF/MSP\"E3H9O6R^DJG>NTQ,*5<-U8V.%[$+X!&;:4\\0Z7(_?W]#Y)="
|
||
|
||
# Spanish based locales
|
||
|
||
ES_LETTERS = {
|
||
"A": 0x2801,
|
||
"B": 0x2803,
|
||
"C": 0x2809,
|
||
"D": 0x2819,
|
||
"E": 0x2811,
|
||
"F": 0x280B,
|
||
"G": 0x281b,
|
||
"H": 0x2813,
|
||
"I": 0x280a,
|
||
"J": 0x281a,
|
||
"K": 0x2805,
|
||
"L": 0x2807,
|
||
"M": 0x280d,
|
||
"N": 0x281d,
|
||
"Ñ": 0x283b,
|
||
"O": 0x2815,
|
||
"P": 0x280f,
|
||
"Q": 0x281f,
|
||
"R": 0x2817,
|
||
"S": 0x280e,
|
||
"T": 0x281e,
|
||
"U": 0x2825,
|
||
"V": 0x2827,
|
||
"W": 0x283a,
|
||
"X": 0x282d,
|
||
"Y": 0x283d,
|
||
"Z": 0x2835,
|
||
}
|
||
|
||
ES_SIGNS = {
|
||
" ": 0x2800, # braille space
|
||
"ª": 0x2801, # ordinal (feminine) -> same as A
|
||
"º": 0x2815, # ordinal (masculine) -> same as O
|
||
"&": 0x282f,
|
||
".": 0x2804,
|
||
",": 0x2802,
|
||
":": 0x2812,
|
||
";": 0x2806,
|
||
"¿": 0x2822,
|
||
"?": 0x2822,
|
||
"¡": 0x2816,
|
||
"!": 0x2816,
|
||
'"': 0x2826,
|
||
"(": 0x2823,
|
||
")": 0x281c,
|
||
# "[": 0x2837, collides with "Á" (Spanish and Catalan)
|
||
# "]": 0x283e, collides with "Ú" (Spanish and Catalan)
|
||
"*": 0x2814,
|
||
|
||
# math
|
||
"-": 0x2824,
|
||
"=": 0x2836,
|
||
"×": 0x2826, # multiplication
|
||
"÷": 0x2832, # division
|
||
"+": 0x2816,
|
||
"@": 0x2810,
|
||
}
|
||
|
||
ES_ACCENT_MARKS = {
|
||
"Á": 0x2837,
|
||
"É": 0x282e,
|
||
"Í": 0x280c,
|
||
"Ó": 0x282c,
|
||
"Ú": 0x283e,
|
||
"Ü": 0x2833,
|
||
}
|
||
|
||
ES_COMBINATIONS = {
|
||
# signs
|
||
"%": (0x2838, 0x2834),
|
||
"‰": (0x2838, 0x2834, 0x2834), # per mile
|
||
"/": (0x2820, 0x2802),
|
||
"\\": (0x2810, 0x2804),
|
||
"<": (0x2810, 0x2805),
|
||
">": (0x2828, 0x2802),
|
||
"|": (0x2838, 0x2807),
|
||
"{": (0x2810, 0x2807),
|
||
"}": (0x2838, 0x2802),
|
||
"–": (0x2824, 0x2824), # two different unicode dashes
|
||
"—": (0x2824, 0x2824),
|
||
"…": (0x2804, 0x2804, 0x2804),
|
||
|
||
# legal
|
||
"©": (0x2823, 0x2828, 0x2809, 0x281c), # copyright
|
||
"®": (0x2823, 0x2828, 0x2817, 0x281c), # registered
|
||
"℗": (0x2823, 0x2828, 0x280f, 0x281c),
|
||
"🄯": (0x2823, 0x2828, 0x2807, 0x281c),
|
||
|
||
# currencies
|
||
"€": (0x2838, 0x2811),
|
||
"$": (0x2838, 0x280e),
|
||
"¢": (0x2818, 0x2809),
|
||
"£": (0x2810, 0x282e),
|
||
"¥": (0x2838, 0x283d),
|
||
"¥": (0x2838, 0x283d),
|
||
}
|
||
|
||
CA_ACCENT_MARKS = {
|
||
"É": 0x283f,
|
||
"Í": 0x280c,
|
||
"Ó": 0x282a,
|
||
"Ú": 0x283e,
|
||
"À": 0x2837,
|
||
"È": 0x282e,
|
||
"Ò": 0x282c,
|
||
"Ï": 0x283b,
|
||
"Ü": 0x2833,
|
||
"Ç": 0x282f,
|
||
}
|
||
|
||
# French based locales
|
||
|
||
FR_LETTERS = {
|
||
"A": 0x2801,
|
||
"B": 0x2803,
|
||
"C": 0x2809,
|
||
"D": 0x2819,
|
||
"E": 0x2811,
|
||
"F": 0x280b,
|
||
"G": 0x281b,
|
||
"H": 0x2813,
|
||
"I": 0x280a,
|
||
"J": 0x281a,
|
||
"K": 0x2805,
|
||
"L": 0x2807,
|
||
"M": 0x280d,
|
||
"N": 0x281d,
|
||
"O": 0x2815,
|
||
"P": 0x280f,
|
||
"Q": 0x281f,
|
||
"R": 0x2817,
|
||
"S": 0x280e,
|
||
"T": 0x281e,
|
||
"U": 0x2825,
|
||
"V": 0x2827,
|
||
"W": 0x283a,
|
||
"X": 0x282d,
|
||
"Y": 0x283d,
|
||
"Z": 0x2835,
|
||
}
|
||
|
||
FR_ACCENT_MARKS = {
|
||
"É": 0x283f,
|
||
"À": 0x2837,
|
||
"È": 0x282e,
|
||
"Ù": 0x283e,
|
||
"Â": 0x2821,
|
||
"Ê": 0x2823,
|
||
"Î": 0x2829,
|
||
"Ô": 0x2839,
|
||
"Û": 0x2831,
|
||
"Ë": 0x282b,
|
||
"Ï": 0x283b,
|
||
"Ü": 0x2833,
|
||
"Ç": 0x282f,
|
||
"Œ": 0x282a, # oe ligature
|
||
}
|
||
|
||
FR_SIGNS = {
|
||
" ": 0x2800, # braille space
|
||
",": 0x2802,
|
||
";": 0x2806,
|
||
":": 0x2812,
|
||
".": 0x2832,
|
||
"?": 0x2822,
|
||
"!": 0x2816,
|
||
"«": 0x2836,
|
||
"»": 0x2836,
|
||
"“": 0x2836,
|
||
"”": 0x2836,
|
||
'"': 0x2836,
|
||
"‘": 0x2836,
|
||
"’": 0x2836,
|
||
"(": 0x2826,
|
||
")": 0x2834,
|
||
"'": 0x2804,
|
||
"'": 0x2804,
|
||
"/": 0x280c,
|
||
"@": 0x281c,
|
||
"^": 0x2808, # elevation exponent
|
||
"-": 0x2824,
|
||
"+": 0x2816,
|
||
"×": 0x2814, # multiplication
|
||
"÷": 0x2812, # division
|
||
"=": 0x2836,
|
||
}
|
||
|
||
FR_COMBINATIONS = {
|
||
"↔": (0x282a, 0x2812, 0x2815), # bidirectional arrow
|
||
"←": (0x282a, 0x2812, 0x2812), # left arrow
|
||
"→": (0x2812, 0x2812, 0x2815), # right arrow
|
||
"…": (0x2832, 0x2832, 0x2832), # unicode ellipsis
|
||
"–": (0x2824, 0x2824),
|
||
"—": (0x2824, 0x2824),
|
||
"_": (0x2810, 0x2824),
|
||
"[": (0x2818, 0x2826),
|
||
"]": (0x2834, 0x2803),
|
||
"°": (0x2810, 0x2815), # degrees
|
||
"§": (0x2810, 0x280f), # paragraph/section symbol
|
||
"&": (0x2810, 0x283f),
|
||
"\\": (0x2810, 0x280c),
|
||
"#": (0x2810, 0x283c),
|
||
"{": (0x2820, 0x2820, 0x2826),
|
||
"}": (0x2834, 0x2804, 0x2804),
|
||
|
||
# math
|
||
"µ": (0x2818, 0x280d), # micron
|
||
"π": (0x2818, 0x280f),
|
||
"≤": (0x2818, 0x2823),
|
||
"≥": (0x2818, 0x281c),
|
||
"<": (0x2810, 0x2823),
|
||
">": (0x2810, 0x281c),
|
||
"~": (0x2810, 0x2822),
|
||
"*": (0x2810, 0x2814),
|
||
"%": (0x2810, 0x282c),
|
||
"‰": (0x2810, 0x282c, 0x282c), # per mile
|
||
|
||
# legal
|
||
"©": (0x2810, 0x2809), # copyright
|
||
"®": (0x2810, 0x2817), # registered
|
||
"™": (0x2810, 0x281e), # trademark
|
||
|
||
# currencies
|
||
"¢": (0x2818, 0x2809),
|
||
"€": (0x2818, 0x2811),
|
||
"£": (0x2818, 0x2807),
|
||
"$": (0x2818, 0x280e),
|
||
"¥": (0x2818, 0x283d),
|
||
"¥": (0x2818, 0x283d),
|
||
}
|
||
|
||
# German based locales
|
||
|
||
DE_ACCENT_MARKS = {
|
||
"Ä": 0x281c,
|
||
"Ö": 0x282a,
|
||
"Ü": 0x2833,
|
||
}
|
||
|
||
DE_SIGNS = {
|
||
" ": 0x2800, # braille space
|
||
",": 0x2802,
|
||
";": 0x2806,
|
||
":": 0x2812,
|
||
"?": 0x2822,
|
||
"!": 0x2816,
|
||
"„": 0x2826,
|
||
"“": 0x2834,
|
||
"§": 0x282c,
|
||
".": 0x2804,
|
||
"–": 0x2824,
|
||
"‚": 0x2820,
|
||
}
|
||
|
||
DE_COMBINATIONS = {
|
||
# signs
|
||
"ß": (0x282e,), # converted to 'SS' if uppercased, so defined in combinations
|
||
"|": (0x2810, 0x2824),
|
||
"[": (0x2818, 0x2837),
|
||
"]": (0x2818, 0x283e),
|
||
"/": (0x2818, 0x280c),
|
||
"`": (0x2820, 0x2826),
|
||
"´": (0x2820, 0x2834),
|
||
"/": (0x2810, 0x2802),
|
||
"&": (0x2810, 0x2825),
|
||
"*": (0x2820, 0x2814),
|
||
"→": (0x2812, 0x2812, 0x2815),
|
||
"←": (0x282a, 0x2812, 0x2812),
|
||
"↔": (0x282a, 0x2812, 0x2812, 0x2815),
|
||
"%": (0x283c, 0x281a, 0x2834),
|
||
"‰": (0x283c, 0x281a, 0x2834, 0x2834),
|
||
"°": (0x2808, 0x2834),
|
||
"′": (0x2808, 0x2814),
|
||
"″": (0x2808, 0x2814, 0x2814),
|
||
"@": (0x2808, 0x281c),
|
||
"_": (0x2808, 0x2838),
|
||
"#": (0x2808, 0x283c),
|
||
|
||
# currencies
|
||
"€": (0x2808, 0x2811),
|
||
"$": (0x2808, 0x280e),
|
||
"¢": (0x2808, 0x2809),
|
||
"£": (0x2808, 0x2807),
|
||
|
||
# legal
|
||
"©": (0x2836, 0x2818, 0x2809, 0x2836),
|
||
"®": (0x2836, 0x2818, 0x2817, 0x2836),
|
||
}
|
||
|
||
# END: UTILITIES
|
||
|
||
# ---------------------------------
|
||
|
||
# LOCALE FUNCTIONS
|
||
|
||
def en_char_map(char):
|
||
"""English chars mapper.
|
||
|
||
Source: https://en.wikipedia.org/wiki/Braille_ASCII#Braille_ASCII_values
|
||
"""
|
||
try:
|
||
mapint = EN_ASCII.index(char.upper())
|
||
except ValueError:
|
||
return char
|
||
return chr(mapint + 0x2800)
|
||
|
||
def numbers_singleuppers_combinations_factory(
|
||
numbers_map,
|
||
singleuppers_map,
|
||
combinations_map, # also individual characters that are modified if uppercased
|
||
number_prefix,
|
||
uppercase_prefix,
|
||
):
|
||
"""Wrapper for various character mappers implementations."""
|
||
def char_mapper(char):
|
||
if char.isnumeric():
|
||
# numeric prefix + number
|
||
return "".join([chr(number_prefix), chr(numbers_map[char])])
|
||
try:
|
||
bcharint = singleuppers_map[char.upper()]
|
||
except KeyError:
|
||
try:
|
||
# combinations
|
||
return "".join([chr(num) for num in combinations_map[char]])
|
||
except KeyError:
|
||
return char
|
||
else:
|
||
# if uppercase, add uppercase prefix before letter
|
||
if char.isupper():
|
||
return "".join([chr(uppercase_prefix), chr(bcharint)])
|
||
return chr(bcharint)
|
||
return char_mapper
|
||
|
||
def es_char_map_loader():
|
||
"""Spanish/Galician chars mappers.
|
||
|
||
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
|
||
"""
|
||
return numbers_singleuppers_combinations_factory(
|
||
LOUIS_BRAILLE_NUMBERS,
|
||
{
|
||
**ES_LETTERS,
|
||
**ES_ACCENT_MARKS,
|
||
**ES_SIGNS,
|
||
**UPPERCASE_PREFIXES,
|
||
},
|
||
ES_COMBINATIONS,
|
||
0x283c,
|
||
0x2828,
|
||
)
|
||
|
||
def eu_char_map_loader():
|
||
"""Euskera chars mapper.
|
||
|
||
Uses the sample implementation as Spanish but without accent marks.
|
||
|
||
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
|
||
"""
|
||
return numbers_singleuppers_combinations_factory(
|
||
LOUIS_BRAILLE_NUMBERS,
|
||
{
|
||
**ES_LETTERS,
|
||
**ES_SIGNS,
|
||
**UPPERCASE_PREFIXES,
|
||
},
|
||
ES_COMBINATIONS,
|
||
0x283c,
|
||
0x2828,
|
||
)
|
||
|
||
def ca_char_map_loader():
|
||
"""Catalan/Valencian chars mappers. Uses the same implementation as
|
||
Spanish but different accent marks.
|
||
|
||
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
|
||
"""
|
||
return numbers_singleuppers_combinations_factory(
|
||
LOUIS_BRAILLE_NUMBERS,
|
||
{
|
||
**ES_LETTERS,
|
||
**CA_ACCENT_MARKS,
|
||
**ES_SIGNS,
|
||
**UPPERCASE_PREFIXES,
|
||
},
|
||
ES_COMBINATIONS,
|
||
0x283c,
|
||
0x2828,
|
||
)
|
||
|
||
def fr_char_map_loader():
|
||
"""French chars mapper.
|
||
|
||
Source: https://sid.usal.es/idocs/F8/FDO12069/signografiabasica.pdf
|
||
"""
|
||
return numbers_singleuppers_combinations_factory(
|
||
LOUIS_BRAILLE_NUMBERS,
|
||
{
|
||
**FR_LETTERS,
|
||
**FR_ACCENT_MARKS,
|
||
**FR_SIGNS,
|
||
**UPPERCASE_PREFIXES,
|
||
},
|
||
FR_COMBINATIONS,
|
||
0x283c,
|
||
0x2828,
|
||
)
|
||
|
||
def de_char_map_loader():
|
||
"""German chars mapper.
|
||
|
||
- For letters, uses the same dictionary as French implementation.
|
||
|
||
Source: http://bskdl.org/textschrift.html
|
||
"""
|
||
return numbers_singleuppers_combinations_factory(
|
||
LOUIS_BRAILLE_NUMBERS,
|
||
{
|
||
**FR_LETTERS, # Same as French implementation
|
||
**DE_ACCENT_MARKS,
|
||
**DE_SIGNS,
|
||
**UPPERCASE_PREFIXES,
|
||
},
|
||
DE_COMBINATIONS,
|
||
0x283c,
|
||
0x2828,
|
||
)
|
||
|
||
# END: LOCALE FUNCTIONS
|
||
|
||
LOCALE_CHARMAPS = {
|
||
"en": en_char_map, # English
|
||
"es": es_char_map_loader, # Spanish
|
||
"fr": fr_char_map_loader, # French
|
||
"de": de_char_map_loader, # German
|
||
"gl": es_char_map_loader, # Galician
|
||
"eu": eu_char_map_loader, # Euskera
|
||
"ca": ca_char_map_loader, # Catalan/Valencian
|
||
}
|
||
|
||
# ---------------------------------
|
||
|
||
# EXTENSION
|
||
|
||
class BrailleL18n(inkex.TextExtension):
|
||
"""Convert to Braille giving a localized map of replacements."""
|
||
def add_arguments(self, parser):
|
||
parser.add_argument(
|
||
"-l", "--locale", type=str, dest="locale", default="en",
|
||
choices=LOCALE_CHARMAPS.keys(),
|
||
help="Locale to use converting to Braille.",
|
||
)
|
||
|
||
def process_chardata(self, text):
|
||
"""Replaceable chardata method for processing the text."""
|
||
chars_mapper = LOCALE_CHARMAPS[self.options.locale]
|
||
|
||
# `chars_mapper` could be a function loader or a characters mapper
|
||
# itself, so check if the characters mapper is loaded and load it
|
||
# if is created from a factory
|
||
if "loader" in chars_mapper.__name__:
|
||
chars_mapper = chars_mapper()
|
||
|
||
return ''.join(map(chars_mapper, text))
|
||
|
||
if __name__ == '__main__':
|
||
BrailleL18n().run() |