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, eliasen@mindspring.com