118 lines
3.5 KiB
Go
118 lines
3.5 KiB
Go
//go:build brlapi && cgo
|
|
|
|
package braille
|
|
|
|
/*
|
|
#cgo pkg-config: liblouis
|
|
#include <liblouis/liblouis.h>
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"unsafe"
|
|
)
|
|
|
|
// translate retains liblouis's output-to-input positions so a routing key
|
|
// addresses the original text rather than a cell in the shortened line.
|
|
func translate(text, table string, cursor int, expand bool) (string, []int, int, error) {
|
|
if table == "" {
|
|
table = defaultContractionTable()
|
|
}
|
|
cTable := C.CString(table)
|
|
defer C.free(unsafe.Pointer(cTable))
|
|
if C.lou_checkTable(cTable) == 0 {
|
|
return "", nil, 0, fmt.Errorf("invalid liblouis table %q", table)
|
|
}
|
|
source := []rune(text)
|
|
in := make([]C.widechar, len(source))
|
|
for index, value := range source {
|
|
in[index] = C.widechar(value)
|
|
}
|
|
// Supply a bounded output buffer; translation fails if a table needs more.
|
|
out := make([]C.widechar, len(source)*8+32)
|
|
inputPos := make([]C.int, len(out))
|
|
outputPos := make([]C.int, len(in))
|
|
inLength, outLength := C.int(len(in)), C.int(len(out))
|
|
cursorPos := C.int(cursor)
|
|
mode := C.int(C.dotsIO)
|
|
if expand && cursor >= 0 && cursor < len(source) && source[cursor] != ' ' {
|
|
mode |= C.compbrlAtCursor
|
|
}
|
|
if C.lou_translate(cTable, &in[0], &inLength, &out[0], &outLength, nil, nil,
|
|
&outputPos[0], &inputPos[0], &cursorPos, mode) == 0 {
|
|
return "", nil, 0, errors.New("liblouis translation failed")
|
|
}
|
|
if int(inLength) != len(in) || outLength < 0 || int(outLength) > len(out) {
|
|
return "", nil, 0, errors.New("liblouis returned invalid translation lengths")
|
|
}
|
|
result := make([]rune, int(outLength))
|
|
positions := make([]int, int(outLength))
|
|
for index := range result {
|
|
if out[index]&0x8000 == 0 || out[index]&^C.widechar(0x80ff) != 0 {
|
|
return "", nil, 0, errors.New("liblouis returned invalid braille dots")
|
|
}
|
|
result[index] = 0x2800 + rune(out[index]&0xff)
|
|
positions[index] = int(inputPos[index])
|
|
if positions[index] < 0 || positions[index] >= len(source) {
|
|
return "", nil, 0, errors.New("liblouis returned invalid position map")
|
|
}
|
|
}
|
|
if cursor == len(source) {
|
|
cursorPos = outLength
|
|
}
|
|
if cursor < 0 {
|
|
cursorPos = -1
|
|
}
|
|
if cursorPos < -1 || cursorPos > outLength {
|
|
return "", nil, 0, errors.New("liblouis returned invalid cursor")
|
|
}
|
|
return string(result), positions, int(cursorPos), nil
|
|
}
|
|
|
|
func defaultContractionTable() string {
|
|
locale := ""
|
|
for _, name := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
|
|
if value := os.Getenv(name); value != "" {
|
|
locale = value
|
|
break
|
|
}
|
|
}
|
|
locale = strings.ToLower(strings.ReplaceAll(strings.Split(strings.Split(locale, ".")[0], "@")[0], "_", "-"))
|
|
if locale == "" || locale == "c" || locale == "posix" {
|
|
locale = "en-us"
|
|
}
|
|
entries, err := os.ReadDir("/usr/share/liblouis/tables")
|
|
if err != nil {
|
|
return "en-us-g2.ctb"
|
|
}
|
|
var candidates []string
|
|
for _, entry := range entries {
|
|
name := strings.ToLower(entry.Name())
|
|
if entry.IsDir() || !strings.HasPrefix(name, locale+"-") ||
|
|
!(strings.HasSuffix(name, ".ctb") || strings.HasSuffix(name, ".utb")) ||
|
|
strings.Contains(name, "interline") || strings.Contains(name, "mathtext") {
|
|
continue
|
|
}
|
|
candidates = append(candidates, entry.Name())
|
|
}
|
|
sort.Strings(candidates)
|
|
for _, grade := range []string{"g2", "g1", "comp6", "comp8"} {
|
|
for _, name := range candidates {
|
|
if strings.Contains(name, grade) {
|
|
return filepath.Join("/usr/share/liblouis/tables", name)
|
|
}
|
|
}
|
|
}
|
|
if len(candidates) > 0 {
|
|
return filepath.Join("/usr/share/liblouis/tables", candidates[0])
|
|
}
|
|
return "en-us-g2.ctb"
|
|
}
|