Workaround for broken CTFontCreateForString

This commit is contained in:
Kovid Goyal 2017-11-11 10:16:43 +05:30
parent f5040532c5
commit 98e93cb4bd
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 28 additions and 10 deletions

View File

@ -123,6 +123,24 @@ ft_face(CTFontRef font, float pt_sz, float xdpi, float ydpi) {
return ans;
}
static inline CTFontRef
find_substitute_face(CFStringRef str, CTFontRef old_font) {
// CTFontCreateForString returns the original font when there are combining
// diacritics in the font and the base character is in the original font,
// so we have to check each character individually
CFIndex len = CFStringGetLength(str), start = 0, amt = len;
while (start < len) {
CTFontRef new_font = CTFontCreateForString(old_font, str, CFRangeMake(start, amt));
if (amt == len && len != 1) amt = 1;
else start++;
if (new_font == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont"); return NULL; }
if (new_font == old_font) { CFRelease(new_font); continue; }
return new_font;
}
PyErr_SetString(PyExc_ValueError, "CoreText returned the same font as a fallback font");
return NULL;
}
static PyObject*
face_for_text(PyObject UNUSED *self, PyObject UNUSED *args) {
char *text;
@ -133,9 +151,9 @@ face_for_text(PyObject UNUSED *self, PyObject UNUSED *args) {
CTFontRef font = PyLong_AsVoidPtr(lp);
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
if (str == NULL) return PyErr_NoMemory();
CFRange range = CFRangeMake(0, CFStringGetLength(str));
CTFontRef new_font = CTFontCreateForString(font, str, range);
if (new_font == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont"); CFRelease(str); return NULL; }
CTFontRef new_font = find_substitute_face(str, font);
CFRelease(str);
if (new_font == NULL) return NULL;
return ft_face(new_font, pt_sz, xdpi, ydpi);
}

View File

@ -231,13 +231,13 @@ def test_render_string(text='Hello, world!', family='monospace', size=144.0, dpi
def test_fallback_font(qtext=None, bold=False, italic=False):
set_font_family(override_dpi=(96.0, 96.0))
for text in (qtext, '你好', 'He\u0347\u0305', '\U0001F929'):
if text:
f = get_fallback_font(text, bold, italic)
try:
print(text, f)
except UnicodeEncodeError:
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
trials = (qtext,) if qtext else ('你好', 'He\u0347\u0305', '\U0001F929')
for text in trials:
f = get_fallback_font(text, bold, italic)
try:
print(text, f)
except UnicodeEncodeError:
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
def showcase():