| # Liblouis Python ctypes bindings |
| # |
| # Copyright (C) 2009, 2010 James Teh <jamie@jantrid.net> |
| # |
| # This file is part of liblouis. |
| # |
| # liblouis is free software: you can redistribute it and/or modify it |
| # under the terms of the GNU Lesser General Public License as published |
| # by the Free Software Foundation, either version 2.1 of the License, or |
| # (at your option) any later version. |
| # |
| # liblouis is distributed in the hope that it will be useful, but |
| # WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # Lesser General Public License for more details. |
| # |
| # You should have received a copy of the GNU Lesser General Public |
| # License along with liblouis. If not, see <http://www.gnu.org/licenses/>. |
| # |
| |
| """Liblouis Python ctypes bindings |
| These bindings allow you to use the liblouis braille translator and back-translator library from within Python. |
| This documentation is only a Python helper. |
| Please see the liblouis documentation for more information. |
| |
| Most of these functions take a C{tableList} argument which specifies |
| a list of translation tables to use. Please see the liblouis documentation |
| concerning the C{tableList} parameter to the C{lou_translateString} |
| function for information about how liblouis searches for these tables. |
| |
| @author: Michael Curran <mick@kulgan.net> |
| @author: James Teh <jamie@jantrid.net> |
| @author: Eitan Isaacson <eitan@ascender.com> |
| @author: Michael Whapples <mwhapples@aim.com> |
| @author: Davy Kager <mail@davykager.nl> |
| @author: Leonard de Ruijter (Babbage B.V.) <alderuijter@gmail.com> |
| """ |
| |
| from __future__ import unicode_literals |
| from ctypes import * |
| import atexit |
| import sys |
| |
| # Some general utility functions |
| def _createTablesString(tablesList): |
| """Creates a tables string for liblouis calls""" |
| return b",".join([x.encode(filesystemencoding) if isinstance(x, str) else bytes(x) for x in tablesList]) |
| |
| def _createTypeformbuf(length, typeform=None): |
| """Creates a typeform buffer for liblouis calls""" |
| return (c_ushort*length)(*typeform) if typeform else (c_ushort*length)() |
| |
| ENCODING_ERROR_HANDLER = None |
| createEncodedByteString = None |
| if sys.version_info.major == 2: |
| ENCODING_ERROR_HANDLER = "strict" |
| createEncodedByteString = lambda x: unicode(x).encode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| else: # sys.version_info[0] == 3 |
| ENCODING_ERROR_HANDLER = "surrogatepass" |
| createEncodedByteString = lambda x: str(x).encode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| |
| try: |
| # Native win32 |
| _loader = windll |
| _functype = WINFUNCTYPE |
| except NameError: |
| # Unix/Cygwin |
| _loader = cdll |
| _functype = CFUNCTYPE |
| liblouis = _loader["###LIBLOUIS_SONAME###"] |
| |
| atexit.register(liblouis.lou_free) |
| |
| liblouis.lou_version.restype = c_char_p |
| |
| liblouis.lou_charSize.restype = c_int |
| |
| liblouis.lou_translateString.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_int), POINTER(c_char), |
| POINTER(c_int), POINTER(c_ushort), POINTER(c_char), c_int) |
| |
| liblouis.lou_translate.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_int), POINTER(c_char), |
| POINTER(c_int), POINTER(c_ushort), POINTER(c_char), |
| POINTER(c_int), POINTER(c_int), POINTER(c_int), c_int) |
| |
| liblouis.lou_backTranslateString.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_int), POINTER(c_char), |
| POINTER(c_int), POINTER(c_ushort), POINTER(c_char), c_int) |
| |
| liblouis.lou_backTranslate.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_int), POINTER(c_char), POINTER(c_int), |
| POINTER(c_ushort), POINTER(c_char), POINTER(c_int), POINTER(c_int), |
| POINTER(c_int), c_int) |
| |
| liblouis.lou_hyphenate.argtypes = ( |
| c_char_p, POINTER(c_char), c_int, POINTER(c_char), c_int) |
| |
| liblouis.lou_checkTable.argtypes = (c_char_p,) |
| |
| liblouis.lou_compileString.argtypes = (c_char_p, c_char_p) |
| |
| liblouis.lou_getTypeformForEmphClass.argtypes = (c_char_p, c_char_p) |
| |
| liblouis.lou_dotsToChar.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_char), |
| c_int, c_int) |
| |
| liblouis.lou_charToDots.argtypes = ( |
| c_char_p, POINTER(c_char), POINTER(c_char), |
| c_int, c_int) |
| |
| LogCallback = _functype(None, c_int, c_char_p) |
| |
| liblouis.lou_registerLogCallback.restype = None |
| |
| liblouis.lou_setLogLevel.restype = None |
| liblouis.lou_setLogLevel.argtypes = (c_int,) |
| |
| #{ Module Configuration |
| #: Specifies the charSize (in bytes) used by liblouis. |
| #: This is fetched once using L{liblouis.lou_charSize}. |
| #: Call it directly, since L{charSize} is not yet defined. |
| #: @type: int |
| wideCharBytes = liblouis.lou_charSize() |
| #: Specifies the number by which the input length should be multiplied |
| #: to calculate the maximum output length. |
| #: @type: int |
| # This default will handle the case where every input character is |
| # undefined in the translation table. |
| outlenMultiplier = 4 + wideCharBytes * 2 |
| #: Specifies the encoding to use when encode/decode filenames |
| #: @type: str |
| filesystemencoding = sys.getfilesystemencoding() |
| #: Specifies the encoding to use when converting from byte strings to unicode strings. |
| #: @type: str |
| conversionEncoding = "utf_%d_le" % (wideCharBytes * 8) |
| #} |
| |
| def version(): |
| """Obtain version information for liblouis. |
| @return: The version of liblouis, plus other information, such as |
| the release date and perhaps notable changes. |
| @rtype: str |
| """ |
| return liblouis.lou_version().decode("ASCII") |
| |
| def charSize(): |
| """Obtain charSize information for liblouis. |
| @return: The size of the widechar with which liblouis was compiled. |
| @rtype: int |
| """ |
| return liblouis.lou_charSize() |
| |
| def translate(tableList, inbuf, typeform=None, cursorPos=0, mode=0): |
| """Translate a string of characters, providing position information. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: The string to translate. |
| @type inbuf: str |
| @param typeform: A list of typeform constants indicating the typeform for each position in inbuf, |
| C{None} for no typeform information. |
| @type typeform: list of int |
| @param cursorPos: The position of the cursor in inbuf. |
| @type cursorPos: int |
| @param mode: The translation mode; add multiple values for a combined mode. |
| @type mode: int |
| @return: A tuple of: the translated string, |
| a list of input positions for each position in the output, |
| a list of output positions for each position in the input, and |
| the position of the cursor in the output. |
| @rtype: (str, list of int, list of int, int) |
| @raise RuntimeError: If a complete translation could not be done. |
| @see: lou_translate in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| inlen = c_int(len(inbuf) // wideCharBytes) |
| outlen = c_int(inlen.value*outlenMultiplier) |
| outbuf = create_string_buffer(outlen.value * wideCharBytes) |
| typeformbuf = None |
| if typeform: |
| typeformbuf = _createTypeformbuf(outlen.value, typeform) |
| inPos = (c_int*outlen.value)() |
| outPos = (c_int*inlen.value)() |
| cursorPos = c_int(cursorPos) |
| if not liblouis.lou_translate(tablesString, inbuf, byref(inlen), |
| outbuf, byref(outlen), typeformbuf, |
| None, outPos, inPos, byref(cursorPos), mode): |
| raise RuntimeError("Can't translate: tables %s, inbuf %s, typeform %s, cursorPos %s, mode %s"%(tableList, inbuf, typeform, cursorPos, mode)) |
| if isinstance(typeform, list): |
| typeform[:] = list(typeformbuf) |
| return outbuf.raw[:outlen.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER), inPos[:outlen.value], outPos[:inlen.value], cursorPos.value |
| |
| def translateString(tableList, inbuf, typeform=None, mode=0): |
| """Translate a string of characters. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: The string to translate. |
| @type inbuf: str |
| @param typeform: A list of typeform constants indicating the typeform for each position in inbuf, |
| C{None} for no typeform information. |
| @type typeform: list of int |
| @param mode: The translation mode; add multiple values for a combined mode. |
| @type mode: int |
| @return: The translated string. |
| @rtype: str |
| @raise RuntimeError: If a complete translation could not be done. |
| @see: lou_translateString in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| inlen = c_int(len(inbuf) // wideCharBytes) |
| outlen = c_int(inlen.value*outlenMultiplier) |
| outbuf = create_string_buffer(outlen.value * wideCharBytes) |
| typeformbuf = None |
| if typeform: |
| typeformbuf = _createTypeformbuf(outlen.value, typeform) |
| if not liblouis.lou_translateString(tablesString, inbuf, byref(inlen), |
| outbuf, byref(outlen), typeformbuf, |
| None, mode): |
| raise RuntimeError("Can't translate: tables %s, inbuf %s, typeform %s, mode %s"%(tableList, inbuf, typeform, mode)) |
| if isinstance(typeform, list): |
| typeform[:] = list(typeformbuf) |
| return outbuf.raw[:outlen.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| |
| def backTranslate(tableList, inbuf, typeform=None, cursorPos=0, mode=0): |
| """Back translates a string of characters, providing position information. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: Braille to back translate. |
| @type inbuf: str |
| @param typeform: List where typeform constants will be placed. |
| @type typeform: list |
| @param cursorPos: Position of cursor. |
| @type cursorPos: int |
| @param mode: Translation mode. |
| @type mode: int |
| @return: A tuple: A string of the back translation, |
| a list of input positions for each position in the output, |
| a list of the output positions for each position in the input and |
| the position of the cursor in the output. |
| @rtype: (str, list of int, list of int, int) |
| @raise RuntimeError: If a complete back translation could not be done. |
| @see: lou_backTranslate in the liblouis documentation. |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| inlen = c_int(len(inbuf) // wideCharBytes) |
| outlen = c_int(inlen.value * outlenMultiplier) |
| outbuf = create_string_buffer(outlen.value * wideCharBytes) |
| typeformbuf = None |
| if isinstance(typeform, list): |
| typeformbuf = _createTypeformbuf(outlen.value) |
| inPos = (c_int*outlen.value)() |
| outPos = (c_int*inlen.value)() |
| cursorPos = c_int(cursorPos) |
| if not liblouis.lou_backTranslate(tablesString, inbuf, byref(inlen), |
| outbuf, byref(outlen), typeformbuf, None, |
| outPos, inPos, byref(cursorPos), mode): |
| raise RuntimeError("Can't back translate: tables %s, inbuf %s, typeform %s, cursorPos %d, mode %d"%(tableList, inbuf, typeform, cursorPos, mode)) |
| if isinstance(typeform, list): |
| typeform[:] = list(typeformbuf) |
| return outbuf.raw[:outlen.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER), inPos[:outlen.value], outPos[:inlen.value], cursorPos.value |
| |
| def backTranslateString(tableList, inbuf, typeform=None, mode=0): |
| """Back translate from Braille. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: The Braille to back translate. |
| @type inbuf: str |
| @param typeform: List for typeform constants to be put in. |
| If you don't want typeform data then give None |
| @type typeform: list |
| @param mode: The translation mode |
| @type mode: int |
| @return: The back translation of inbuf. |
| @rtype: str |
| @raise RuntimeError: If a complete back translation could not be done. |
| @see: lou_backTranslateString in the liblouis documentation. |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| inlen = c_int(len(inbuf) // wideCharBytes) |
| outlen = c_int(inlen.value * outlenMultiplier) |
| outbuf = create_string_buffer(outlen.value * wideCharBytes) |
| typeformbuf = None |
| if isinstance(typeform, list): |
| typeformbuf = _createTypeformbuf(outlen.value) |
| if not liblouis.lou_backTranslateString(tablesString, inbuf, byref(inlen), outbuf, |
| byref(outlen), typeformbuf, None, mode): |
| raise RuntimeError("Can't back translate: tables %s, inbuf %s, mode %d"%(tableList, inbuf, mode)) |
| if isinstance(typeform, list): |
| typeform[:] = list(typeformbuf) |
| return outbuf.raw[:outlen.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| |
| def hyphenate(tableList, inbuf, mode=0): |
| """Get information for hyphenation. |
| @param tableList: A list of translation tables and hyphenation |
| dictionaries. |
| @type tableList: list of str |
| @param inbuf: The text to get hyphenation information about. |
| This should be a single word and leading/trailing whitespace |
| and punctuation is ignored. |
| @type inbuf: str |
| @param mode: Lets liblouis know if inbuf is plain text or Braille. |
| Set to 0 for text and anyother value for Braille. |
| @type mode: int |
| @return: A string with '1' at the beginning of every syllable |
| and '0' elsewhere. |
| @rtype: str |
| @raise RuntimeError: If hyphenation data could not be produced. |
| @see: lou_hyphenate in the liblouis documentation. |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| inlen = c_int(len(inbuf) // wideCharBytes) |
| hyphen_string = create_string_buffer(inlen.value + 1) |
| if not liblouis.lou_hyphenate(tablesString, inbuf, inlen, hyphen_string, mode): |
| raise RuntimeError("Can't hyphenate: tables %s, inbuf %s, mode %d"%(tableList, inbuf, mode)) |
| return hyphen_string.value.decode("ASCII") |
| |
| def checkTable(tableList): |
| """Check if the specified tables can be found and compiled. |
| This can be used to check if a list of tables contains errors |
| before sending it to other liblouis functions |
| that accept a list of tables. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @raise RuntimeError: If compilation failed. |
| @see: lou_checkTable in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| if not liblouis.lou_checkTable(tablesString): |
| raise RuntimeError("Can't compile: tables %s"%tableList) |
| |
| def compileString(tableList, inString): |
| """Compile a table entry on the fly at run-time. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inString: The table entry to be added. |
| @type inString: str |
| @raise RuntimeError: If compilation of the entry failed. |
| @see: lou_compileString in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| inBytes = inString.encode("ASCII") if isinstance(inString, str) else bytes(inString) |
| if not liblouis.lou_compileString(tablesString, inString): |
| raise RuntimeError("Can't compile entry: tables %s, inString %s"%(tableList, inString)) |
| |
| def getTypeformForEmphClass(tableList, emphClass): |
| """Get the typeform bit for the named emphasis class. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param emphClass: An emphasis class name. |
| @type emphClass: str |
| @see: lou_getTypeformForEmphClass in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| return liblouis.lou_getTypeformForEmphClass(tablesString, emphClass) |
| |
| def dotsToChar(tableList, inbuf): |
| """"Convert a string of dot patterns to a string of characters according to the specifications in tableList. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: a string of dot patterns, either in liblouis format or Unicode braille. |
| @type inbuf: str |
| @raise RuntimeError: If a complete conversion could not be done. |
| @see: lou_dotsToChar in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| length = c_int(len(inbuf) // wideCharBytes) |
| outbuf = create_string_buffer(length.value * wideCharBytes) |
| if not liblouis.lou_dotsToChar(tablesString, inbuf, outbuf, length, 0): |
| raise RuntimeError("Can't convert dots to char: tables %s, inbuf %s"%(tableList, inbuf)) |
| return outbuf.raw[:length.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| |
| def charToDots(tableList, inbuf, mode=0): |
| """"Convert a string of characterss to a string of dot patterns according to the specifications in tableList. |
| @param tableList: A list of translation tables. |
| @type tableList: list of str |
| @param inbuf: a string of characters. |
| @type inbuf: str |
| @param mode: The translation mode; add multiple values for a combined mode. |
| @type mode: int |
| @raise RuntimeError: If a complete conversion could not be done. |
| @see: lou_charToDOts in the liblouis documentation |
| """ |
| tablesString = _createTablesString(tableList) |
| inbuf = createEncodedByteString(inbuf) |
| length = c_int(len(inbuf) // wideCharBytes) |
| outbuf = create_string_buffer(length.value * wideCharBytes) |
| if not liblouis.lou_charToDots(tablesString, inbuf, outbuf, length, mode): |
| raise RuntimeError("Can't convert char to dots: tables %s, inbuf %s, mode %d"%(tableList, inbuf, mode)) |
| return outbuf.raw[:length.value * wideCharBytes].decode(conversionEncoding, errors=ENCODING_ERROR_HANDLER) |
| |
| def registerLogCallback(logCallback): |
| """Register logging callbacks. |
| Set to C{None} for default callback. |
| @param logCallback: The callback to use. |
| The callback must take two arguments: |
| @param level: The log level on which a message is logged. |
| @type level: int |
| @param message: The logged message. |
| Note that the callback should provide its own ASCII decoding routine. |
| @type message: bytes |
| |
| Example callback: |
| |
| @louis.LogCallback |
| def incomingLouisLog(level, message): |
| print("Message %s logged at level %d" % (message.decode("ASCII"), level)) |
| |
| @type logCallback: L{LogCallback} |
| """ |
| if logCallback is not None and not isinstance(logCallback, LogCallback): |
| raise TypeError("logCallback should be of type {} or NoneType".format(LogCallback.__name__)) |
| return liblouis.lou_registerLogCallback(logCallback) |
| |
| def setLogLevel(level): |
| """Set the level for logging callback to be called at. |
| @param level: one of the C{LOG_*} constants. |
| @type level: int |
| @raise ValueError: If an invalid log level is provided. |
| """ |
| if level not in logLevels: |
| raise ValueError("Level %d is an invalid log level"%level) |
| return liblouis.lou_setLogLevel(level) |
| |
| #{ Typeforms |
| plain_text = 0x0000 |
| emph_1 = comp_emph_1 = italic = 0x0001 |
| emph_2 = comp_emph_2 = underline = 0x0002 |
| emph_3 = comp_emph_3 = bold = 0x0004 |
| emph_4 = 0x0008 |
| emph_5 = 0x0010 |
| emph_6 = 0x0020 |
| emph_7 = 0x0040 |
| emph_8 = 0x0080 |
| emph_9 = 0x0100 |
| emph_10 = 0x0200 |
| computer_braille = 0x0400 |
| no_translate = 0x0800 |
| no_contract = 0x1000 |
| #} |
| |
| #{ Translation modes |
| noContractions = 1 |
| compbrlAtCursor = 2 |
| dotsIO = 4 |
| compbrlLeftCursor = 32 |
| ucBrl = 64 |
| noUndefined = 128 |
| noUndefinedDots = noUndefined # alias for backward compatiblity |
| partialTrans = 256 |
| #} |
| |
| #{ logLevels |
| LOG_ALL = 0 |
| LOG_DEBUG = 10000 |
| LOG_INFO = 20000 |
| LOG_WARN = 30000 |
| LOG_ERROR = 40000 |
| LOG_FATAL = 50000 |
| LOG_OFF = 60000 |
| #} |
| |
| logLevels = (LOG_ALL, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL, LOG_OFF) |
| |
| if __name__ == '__main__': |
| # Just some common tests. |
| print(version()) |
| print(translate([b'../tables/en-us-g2.ctb'], 'Hello world!', cursorPos=5)) |