Download or view Braille.frink in plain text format
// This is a library that converts text to Grade 1 Braille.
//
// US Standards for spacing, etc are given here:
// https://www.loc.gov/nls/about/organization/standards-guidelines/contract-specifications/
//
// Specifically these spacings are for books and pamphlets as found in
// Publication 800:2014 Braille Books and Pamphlets
// https://www.loc.gov/nls/wp-content/uploads/2019/09/Spec800.11October2014.final_.pdf
//
// ADA Accessibilty standards for signage are found in section 703.3 of this:
// https://www.access-board.gov/ada/#ada-703_3
//
// US ADA Signage quick reference:
// https://www.accentsignage.com/wp-content/uploads/ADA-Quick-Reference.pdf
//
// Preview of ISO Standard 17049:
// https://www.sis.se/api/document/preview/916703/
//
class Braille
{
// A dictionary mapping single characters to their braille Unicode
// equivalent.
class var charDict = undef
// An inverse dictionary mapping Unicode characters to their ASCII
// equivalents
class var reverseDict = undef
// Nominal base diameter of braille dots.
class var DOT_DIAMETER = 0.057 in
// Nominal distance from center to center of adjacent dots in the same
// cell.
class var DOT_SPACING = 0.092 in
// Nominal horizontal distance between corresponding dots in two cells
// horizontally (empirically found to match my braille template.)
// The official specification section 3.2.3 says 0.245 +0.005/-0.001 inches.
class var INTERCHAR_HORIZONTAL_SPACING = 0.241589 in
// Nominal vertical distance between corresponding dots in two cells
class var INTERLINE_SPACING = 0.400 in
// Initialize the character dictionaries.
class initDict[] :=
{
charDict = new dict
charDict@"a" = "\u2801"
charDict@"b" = "\u2803"
charDict@"c" = "\u2809"
charDict@"d" = "\u2819"
charDict@"e" = "\u2811"
charDict@"f" = "\u280b"
charDict@"g" = "\u281b"
charDict@"h" = "\u2813"
charDict@"i" = "\u280a"
charDict@"j" = "\u281a"
charDict@"k" = "\u2805"
charDict@"l" = "\u2807"
charDict@"m" = "\u280d"
charDict@"n" = "\u281d"
charDict@"o" = "\u2815"
charDict@"p" = "\u280f"
charDict@"q" = "\u281f"
charDict@"r" = "\u2817"
charDict@"s" = "\u280e"
charDict@"t" = "\u281e"
charDict@"u" = "\u2825"
charDict@"v" = "\u2827"
charDict@"w" = "\u283a"
charDict@"x" = "\u282d"
charDict@"y" = "\u283d"
charDict@"z" = "\u2835"
charDict@"?" = "\u2826"
charDict@"," = "\u2802"
charDict@";" = "\u2806"
charDict@":" = "\u2812"
charDict@"." = "\u2832"
charDict@"!" = "\u2816"
charDict@"-" = "\u2824"
// We invert here because numbers and letters use the same symbols.
reverseDict = charDict.invert[]
charDict@"1" = "\u2801"
charDict@"2" = "\u2803"
charDict@"3" = "\u2809"
charDict@"4" = "\u2819"
charDict@"5" = "\u2811"
charDict@"6" = "\u280b"
charDict@"7" = "\u281b"
charDict@"8" = "\u2813"
charDict@"9" = "\u280a"
charDict@"0" = "\u281a"
}
// Convert the specified string to its Braille unicode equivalent.
// This just uses Braille grade 1 transformations.
class toBrailleGrade1[str] :=
{
if charDict == undef
initDict[]
result = ""
// Collapse multiple spaces into one.
str =~ %s/([\s]+)/ /g
// Add number sign before numbers
str =~ %s/([0-9]+)/\u283c$1/g;
// Add capitalization mark for next character.
str =~ %s/([A-Z])/"\u2820" + lc[$1]/ge;
for c = charList[str]
{
if charDict.containsKey[c]
result = result + charDict@c
else
result = result + c
}
return result
}
// Draw a block of text in braille, wrapping it to fit the specified maximum
// width.
//
// Parameters:
// g : an object of type graphics that will be drawn to.
// str: The string to draw. If it is not already converted to Unicode
// representation, it will be automatically converted.
// x,y: The coordinates where drawing is to begin (must have dimensions
// of length (e.g. "0 mm, 0 mm") unless you redefine the spacing
// constants.
// maxWidth: The maximum width to draw before wrapping. Should have
// dimensions of width unless you redefine the spacing constants.
// If this is undef, then no wrapping will be performed.
// hDirection: Should be 1 for drawing normally. To draw horizontally
// mirrored (so you can print on the back of the paper and punch
// through it,) this should be -1.
// fill: true if the dots are to be filled. If false, only the outline
// of the dot will be drawn.
// diam: The diameter of each dot.
// dotSpace: The distance between two dots in a character.
// horizSpace: The horizontal distance between corresponding dots in two
// different characters.
// lineSpace: The vertical distance between corresponding dots in two
// different lines of text.
//
// Spacing constants follow standard sizes for printed braille.
//
class drawText[g is graphics, str, x, y, maxWidth=undef, hDirection = 1, fill=true, diam = Braille.DOT_DIAMETER, dotSpace = Braille.DOT_SPACING, horizSpace = Braille.INTERCHAR_HORIZONTAL_SPACING, lineSpace = Braille.INTERLINE_SPACING] :=
{
if ! isEncoded[str]
str = toBrailleGrade1[str]
if maxWidth == undef
drawLine[g, str, x, y, hDirection, fill, diam, dotSpace, horizSpace]
else
{
maxChars = maxWidth div horizSpace
// println["maxChars is $maxChars"]
startPos = 0
length = length[str]
endPos = min[startPos + maxChars - 1, length]
while startPos < length
{
while (endPos >= startPos)
{
if substrLen[str, endPos, 1] == " "
{
text = substr[str, startPos, endPos]
// println[text]
drawLine[g, text, x, y, hDirection, fill, diam, dotSpace, horizSpace]
y = y + lineSpace
startPos = endPos + 1
endPos = min[startPos + maxChars - 1, length]
} else
endPos = endPos - 1
}
if (endPos == startPos-1) // Didn't find a space
{
text = substrLen[str, startPos, maxChars]
// println[text]
drawLine[g, text, x, y, hDirection, fill, diam, dotSpace, horizSpace]
y = y + lineSpace
startPos = startPos + maxChars
endPos = min[startPos + maxChars-1, length]
}
}
}
}
// Draws a line of text in Braille. You probably shouldn't call this
// method directly, but rather call drawText which performs the appropriate
// wrapping and encoding.
class drawLine[g is graphics, str, x, y, hDirection = 1, fill=true, diam = Braille.DOT_DIAMETER, dotSpace = Braille.DOT_SPACING, horizSpace = Braille.INTERCHAR_HORIZONTAL_SPACING] :=
{
x = x + hDirection * (horizSpace - dotSpace)/2 // Center in space
for c = charList[str]
{
drawChar[g, c, x, y, hDirection, fill, diam, dotSpace, horizSpace]
x = x + horizSpace * hDirection
}
}
// Draws a single braille character.
class drawChar[g is graphics, char, x, y, hDirection = 1, fill = true, diam = Braille.DOT_DIAMETER, dotSpace = Braille.DOT_SPACING, horizSpace = Braille.INTERCHAR_HORIZONTAL_SPACING] :=
{
c = char[char]
if c >= 0x2800 and c <= 0x28FF
{
c = c - 0x2800
if getBit[c, 0] == 1
drawDot[g, x, y, fill, diam]
if getBit[c, 1] == 1
drawDot[g, x, y+dotSpace, fill, diam]
if getBit[c, 2] == 1
drawDot[g, x, y+dotSpace*2, fill, diam]
if getBit[c, 3] == 1
drawDot[g, x+dotSpace*hDirection, y, fill, diam]
if getBit[c, 4] == 1
drawDot[g, x+dotSpace*hDirection, y+dotSpace, fill, diam]
if getBit[c, 5] == 1
drawDot[g, x+dotSpace*hDirection, y+dotSpace*2, fill, diam]
if getBit[c, 6] == 1
drawDot[g, x, y+dotSpace*3, fill, diam]
if getBit[c, 7] == 1
drawDot[g, x+dotSpace*hDirection, y+dotSpace*3, fill, diam]
}
}
// Draw a single dot centered at the specified location.
class drawDot[g is graphics, x, y, fill=true, diam = Braille.DOT_DIAMETER] :=
{
if fill
g.fillEllipseCenter[x, y, diam, diam]
else
g.drawEllipseCenter[x, y, diam, diam]
}
// Returns true if the string is already encoded into Braille, (that is,
// if it contains any Braille Unicode characters at all.)
class isEncoded[str] :=
{
for c = chars[str]
if c >= 0x2800 and c <= 0x28ff
return true
return false
}
// This reverses
class fromUnicodeBraille[str] :=
{
result = ""
if charDict == undef
initDict[]
for c = charList[str]
if reverseDict@c == undef
result = result + c
else
result = result + reverseDict@c
return result
}
}
Download or view Braille.frink in plain text format
This is a program written in the programming language Frink.
For more information, view the Frink
Documentation or see More Sample Frink Programs.
Alan Eliasen was born 20217 days, 15 hours, 10 minutes ago.