mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Edit Book: Add support for showing the names of non-BMP characters on narrow python builds (windows and OS X)
This commit is contained in:
parent
3901051e2e
commit
22ca2bcac4
@ -17,21 +17,12 @@ from PyQt4.Qt import (
|
|||||||
QStyledItemDelegate, QSplitter, QLabel, QSizePolicy, QIcon, QMimeData,
|
QStyledItemDelegate, QSplitter, QLabel, QSizePolicy, QIcon, QMimeData,
|
||||||
QPushButton, QToolButton, QInputMethodEvent)
|
QPushButton, QToolButton, QInputMethodEvent)
|
||||||
|
|
||||||
from calibre.constants import ispy3, plugins, cache_dir
|
from calibre.constants import plugins, cache_dir
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.widgets2 import HistoryLineEdit2
|
from calibre.gui2.widgets2 import HistoryLineEdit2
|
||||||
from calibre.gui2.tweak_book import tprefs
|
from calibre.gui2.tweak_book import tprefs
|
||||||
from calibre.gui2.tweak_book.widgets import Dialog
|
from calibre.gui2.tweak_book.widgets import Dialog
|
||||||
|
from calibre.utils.icu import safe_chr as chr
|
||||||
if not ispy3:
|
|
||||||
if sys.maxunicode >= 0x10FFFF:
|
|
||||||
chr = unichr
|
|
||||||
else:
|
|
||||||
def chr(i):
|
|
||||||
# Narrow builds of python cannot represent code point > 0xffff as a
|
|
||||||
# single character, so we need our own implementation of unichr
|
|
||||||
# that returns them as a surrogate pair
|
|
||||||
return (b"\U%s" % (hex(i)[2:].zfill(8))).decode('unicode-escape')
|
|
||||||
|
|
||||||
ROOT = QModelIndex()
|
ROOT = QModelIndex()
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import unicodedata, os
|
import os
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from future_builtins import map
|
from future_builtins import map
|
||||||
@ -33,6 +33,7 @@ from calibre.gui2.tweak_book.toc import TOCViewer
|
|||||||
from calibre.gui2.tweak_book.char_select import CharSelect
|
from calibre.gui2.tweak_book.char_select import CharSelect
|
||||||
from calibre.gui2.tweak_book.editor.widget import register_text_editor_actions
|
from calibre.gui2.tweak_book.editor.widget import register_text_editor_actions
|
||||||
from calibre.gui2.tweak_book.editor.insert_resource import InsertImage
|
from calibre.gui2.tweak_book.editor.insert_resource import InsertImage
|
||||||
|
from calibre.utils.icu import character_name
|
||||||
|
|
||||||
def open_donate():
|
def open_donate():
|
||||||
open_url(QUrl('http://calibre-ebook.com/donate'))
|
open_url(QUrl('http://calibre-ebook.com/donate'))
|
||||||
@ -188,7 +189,7 @@ class CursorPositionWidget(QWidget): # {{{
|
|||||||
self.la.setText('')
|
self.la.setText('')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
name = unicodedata.name(character, None) if character and tprefs['editor_show_char_under_cursor'] else None
|
name = character_name(character) if character and tprefs['editor_show_char_under_cursor'] else None
|
||||||
except Exception:
|
except Exception:
|
||||||
name = None
|
name = None
|
||||||
text = _('Line: {0} : {1}').format(line, col)
|
text = _('Line: {0} : {1}').format(line, col)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#define UNICODE
|
#define UNICODE
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <unicode/uversion.h>
|
||||||
#include <unicode/utypes.h>
|
#include <unicode/utypes.h>
|
||||||
#include <unicode/uclean.h>
|
#include <unicode/uclean.h>
|
||||||
|
#include <unicode/utf16.h>
|
||||||
#include <unicode/ucol.h>
|
#include <unicode/ucol.h>
|
||||||
#include <unicode/ucoleitr.h>
|
#include <unicode/ucoleitr.h>
|
||||||
#include <unicode/ustring.h>
|
#include <unicode/ustring.h>
|
||||||
@ -672,6 +674,7 @@ icu_set_filesystem_encoding(PyObject *self, PyObject *args) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// set_default_encoding {{{
|
// set_default_encoding {{{
|
||||||
static PyObject *
|
static PyObject *
|
||||||
icu_get_available_transliterators(PyObject *self, PyObject *args) {
|
icu_get_available_transliterators(PyObject *self, PyObject *args) {
|
||||||
@ -701,6 +704,59 @@ icu_get_available_transliterators(PyObject *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
// character_name {{{
|
||||||
|
static PyObject *
|
||||||
|
icu_character_name(PyObject *self, PyObject *args) {
|
||||||
|
char *input = NULL, name[512] = {0};
|
||||||
|
int32_t sz, alias = 0;
|
||||||
|
UChar *buf;
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
PyObject *palias = NULL;
|
||||||
|
UChar32 code = 0;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "es|O", "UTF-8", &input, &palias)) return NULL;
|
||||||
|
|
||||||
|
if (palias != NULL && PyObject_IsTrue(palias)) alias = 1;
|
||||||
|
|
||||||
|
sz = (int32_t)strlen(input);
|
||||||
|
buf = (UChar*)calloc(sz*4 + 1, sizeof(UChar));
|
||||||
|
if (buf == NULL) { PyErr_NoMemory(); goto end; }
|
||||||
|
u_strFromUTF8(buf, sz*4, &sz, input, sz, &status);
|
||||||
|
if (U_FAILURE(status)) { PyErr_SetString(PyExc_ValueError, "Failed to decode char string"); goto end; }
|
||||||
|
U16_GET(buf, 0, 0, -1, code);
|
||||||
|
if (alias) {
|
||||||
|
sz = u_charName(code, U_CHAR_NAME_ALIAS, name, 511, &status);
|
||||||
|
} else {
|
||||||
|
sz = u_charName(code, U_UNICODE_CHAR_NAME, name, 511, &status);
|
||||||
|
}
|
||||||
|
if (U_FAILURE(status)) { PyErr_SetString(PyExc_ValueError, "Failed to get name for code"); goto end; }
|
||||||
|
end:
|
||||||
|
if (buf != NULL) free(buf);
|
||||||
|
PyMem_Free(input);
|
||||||
|
|
||||||
|
return (PyErr_Occurred()) ? NULL : Py_BuildValue("s#", name, sz);
|
||||||
|
} // }}}
|
||||||
|
|
||||||
|
// chr {{{
|
||||||
|
static PyObject *
|
||||||
|
icu_chr(PyObject *self, PyObject *args) {
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
UChar32 code = 0;
|
||||||
|
UChar buf[5] = {0};
|
||||||
|
int32_t sz = 0;
|
||||||
|
char utf8[21];
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "I", &code)) return NULL;
|
||||||
|
|
||||||
|
u_strFromUTF32(buf, 4, &sz, &code, 1, &status);
|
||||||
|
if (U_FAILURE(status)) { PyErr_SetString(PyExc_ValueError, "arg not in range(0x110000)"); goto end; }
|
||||||
|
u_strToUTF8(utf8, 20, &sz, buf, sz, &status);
|
||||||
|
if (U_FAILURE(status)) { PyErr_SetString(PyExc_ValueError, "arg not in range(0x110000)"); goto end; }
|
||||||
|
end:
|
||||||
|
return (PyErr_Occurred()) ? NULL : Py_BuildValue("s#", utf8, sz);
|
||||||
|
} // }}}
|
||||||
|
|
||||||
static PyMethodDef icu_methods[] = {
|
static PyMethodDef icu_methods[] = {
|
||||||
{"upper", icu_upper, METH_VARARGS,
|
{"upper", icu_upper, METH_VARARGS,
|
||||||
"upper(locale, unicode object) -> upper cased unicode object using locale rules."
|
"upper(locale, unicode object) -> upper cased unicode object using locale rules."
|
||||||
@ -726,6 +782,14 @@ static PyMethodDef icu_methods[] = {
|
|||||||
"get_available_transliterators() -> Return list of available transliterators. This list is rather limited on OS X."
|
"get_available_transliterators() -> Return list of available transliterators. This list is rather limited on OS X."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{"character_name", icu_character_name, METH_VARARGS,
|
||||||
|
"character_name(char, alias=False) -> Return name for the first character in char, which must be a unicode string."
|
||||||
|
},
|
||||||
|
|
||||||
|
{"chr", icu_chr, METH_VARARGS,
|
||||||
|
"chr(code) -> Return a python unicode string corresponding to the specified character code. The string can have length 1 or 2 (for non BMP codes on narrow python builds)."
|
||||||
|
},
|
||||||
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -735,13 +799,20 @@ PyMODINIT_FUNC
|
|||||||
initicu(void)
|
initicu(void)
|
||||||
{
|
{
|
||||||
PyObject* m;
|
PyObject* m;
|
||||||
|
UVersionInfo ver, uver;
|
||||||
UErrorCode status = U_ZERO_ERROR;
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
char version[U_MAX_VERSION_STRING_LENGTH+1] = {0};
|
||||||
|
char uversion[U_MAX_VERSION_STRING_LENGTH+5] = {0};
|
||||||
|
|
||||||
u_init(&status);
|
u_init(&status);
|
||||||
if (U_FAILURE(status)) {
|
if (U_FAILURE(status)) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, u_errorName(status));
|
PyErr_SetString(PyExc_RuntimeError, u_errorName(status));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
u_getVersion(ver);
|
||||||
|
u_versionToString(ver, version);
|
||||||
|
u_getUnicodeVersion(uver);
|
||||||
|
u_versionToString(uver, uversion);
|
||||||
|
|
||||||
if (PyType_Ready(&icu_CollatorType) < 0)
|
if (PyType_Ready(&icu_CollatorType) < 0)
|
||||||
return;
|
return;
|
||||||
@ -753,6 +824,8 @@ initicu(void)
|
|||||||
PyModule_AddObject(m, "Collator", (PyObject *)&icu_CollatorType);
|
PyModule_AddObject(m, "Collator", (PyObject *)&icu_CollatorType);
|
||||||
// uint8_t must be the same size as char
|
// uint8_t must be the same size as char
|
||||||
PyModule_AddIntConstant(m, "ok", (U_SUCCESS(status) && sizeof(uint8_t) == sizeof(char)) ? 1 : 0);
|
PyModule_AddIntConstant(m, "ok", (U_SUCCESS(status) && sizeof(uint8_t) == sizeof(char)) ? 1 : 0);
|
||||||
|
PyModule_AddStringConstant(m, "icu_version", version);
|
||||||
|
PyModule_AddStringConstant(m, "unicode_version", uversion);
|
||||||
|
|
||||||
ADDUCONST(USET_SPAN_NOT_CONTAINED);
|
ADDUCONST(USET_SPAN_NOT_CONTAINED);
|
||||||
ADDUCONST(USET_SPAN_CONTAINED);
|
ADDUCONST(USET_SPAN_CONTAINED);
|
||||||
|
@ -130,6 +130,34 @@ def py_find(pattern, source):
|
|||||||
return pos, len(pattern)
|
return pos, len(pattern)
|
||||||
return -1, -1
|
return -1, -1
|
||||||
|
|
||||||
|
def character_name(string):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
return _icu.character_name(unicode(string)).decode('utf-8') or None
|
||||||
|
except AttributeError:
|
||||||
|
import unicodedata
|
||||||
|
return unicodedata.name(unicode(string)[0], None)
|
||||||
|
except (TypeError, ValueError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if sys.maxunicode >= 0x10ffff:
|
||||||
|
try:
|
||||||
|
py_safe_chr = unichr
|
||||||
|
except NameError:
|
||||||
|
py_safe_chr = chr
|
||||||
|
else:
|
||||||
|
def py_safe_chr(i):
|
||||||
|
# Narrow builds of python cannot represent code point > 0xffff as a
|
||||||
|
# single character, so we need our own implementation of unichr
|
||||||
|
# that returns them as a surrogate pair
|
||||||
|
return (b"\U%s" % (hex(i)[2:].zfill(8))).decode('unicode-escape')
|
||||||
|
|
||||||
|
def safe_chr(code):
|
||||||
|
try:
|
||||||
|
return _icu.chr(code)
|
||||||
|
except AttributeError:
|
||||||
|
return py_safe_chr(code)
|
||||||
|
|
||||||
def icu_find(collator, pattern, source):
|
def icu_find(collator, pattern, source):
|
||||||
try:
|
try:
|
||||||
return collator.find(pattern, source)
|
return collator.find(pattern, source)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user