kitty/kitty/core_text.m

158 lines
5.7 KiB
Objective-C

/*
* core_text.c
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "fonts.h"
#include <structmember.h>
#include <stdint.h>
#include <math.h>
#import <CoreGraphics/CGBitmapContext.h>
#import <CoreText/CTFont.h>
#include <Foundation/Foundation.h>
#include <CoreText/CoreText.h>
#import <Foundation/NSString.h>
#import <Foundation/NSDictionary.h>
static inline char*
convert_cfstring(CFStringRef src, int free_src) {
#define SZ 2048
static char buf[SZ+2] = {0};
bool ok = false;
if(!CFStringGetCString(src, buf, SZ, kCFStringEncodingUTF8)) PyErr_SetString(PyExc_ValueError, "Failed to convert CFString");
else ok = true;
if (free_src) CFRelease(src);
return ok ? buf : NULL;
#undef SZ
}
static PyObject*
font_descriptor_to_python(CTFontDescriptorRef descriptor) {
NSURL *url = (NSURL *) CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
NSString *psName = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);
NSString *family = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute);
NSString *style = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute);
NSDictionary *traits = (NSDictionary *) CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute);
unsigned int straits = [traits[(id)kCTFontSymbolicTrait] unsignedIntValue];
NSNumber *weightVal = traits[(id)kCTFontWeightTrait];
NSNumber *widthVal = traits[(id)kCTFontWidthTrait];
PyObject *ans = Py_BuildValue("{ssssssss sOsOsO sfsfsI}",
"path", [[url path] UTF8String],
"postscript_name", [psName UTF8String],
"family", [family UTF8String],
"style", [style UTF8String],
"bold", (straits & kCTFontBoldTrait) != 0 ? Py_True : Py_False,
"italic", (straits & kCTFontItalicTrait) != 0 ? Py_True : Py_False,
"monospace", (straits & kCTFontMonoSpaceTrait) != 0 ? Py_True : Py_False,
"weight", [weightVal floatValue],
"width", [widthVal floatValue],
"traits", straits
);
[url release];
[psName release];
[family release];
[style release];
[traits release];
return ans;
}
static CTFontDescriptorRef
font_descriptor_from_python(PyObject *src) {
CTFontSymbolicTraits symbolic_traits = 0;
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
PyObject *t = PyDict_GetItemString(src, "traits");
if (t == NULL) {
symbolic_traits = (
(PyDict_GetItemString(src, "bold") == Py_True) ? kCTFontBoldTrait : 0 |
(PyDict_GetItemString(src, "italic") == Py_True) ? kCTFontItalicTrait : 0 |
(PyDict_GetItemString(src, "monospace") == Py_True) ? kCTFontMonoSpaceTrait : 0);
} else {
symbolic_traits = PyLong_AsUnsignedLong(t);
}
NSDictionary *traits = @{(id)kCTFontSymbolicTrait:[NSNumber numberWithUnsignedInt:symbolic_traits]};
attrs[(id)kCTFontTraitsAttribute] = traits;
#define SET(x, attr) \
t = PyDict_GetItemString(src, #x); \
if (t) attrs[(id)attr] = [NSString stringWithUTF8String:PyUnicode_AsUTF8(t)];
SET(family, kCTFontFamilyNameAttribute);
SET(style, kCTFontStyleNameAttribute);
SET(postscript_name, kCTFontNameAttribute);
#undef SET
return CTFontDescriptorCreateWithAttributes((CFDictionaryRef) attrs);
}
PyObject*
coretext_all_fonts(PyObject UNUSED *_self) {
static CTFontCollectionRef collection = NULL;
if (collection == NULL) collection = CTFontCollectionCreateFromAvailableFonts(NULL);
NSArray *matches = (NSArray *) CTFontCollectionCreateMatchingFontDescriptors(collection);
PyObject *ans = PyTuple_New([matches count]), *temp;
if (ans == NULL) return PyErr_NoMemory();
for (unsigned int i = 0; i < [matches count]; i++) {
temp = font_descriptor_to_python((CTFontDescriptorRef) matches[i]);
if (temp == NULL) { Py_DECREF(ans); return NULL; }
PyTuple_SET_ITEM(ans, i, temp); temp = NULL;
}
return ans;
}
static PyObject*
face_for_text(PyObject UNUSED *self, PyObject UNUSED *args) {
Py_RETURN_NONE; // TODO: Implement this
}
static void
free_font(void *f) {
CFRelease((CTFontRef)f);
}
static PyObject*
create_face(PyObject UNUSED *self, PyObject *args) {
PyObject *descriptor;
float point_sz, xdpi, ydpi;
if(!PyArg_ParseTuple(args, "Offf", &descriptor, &point_sz, &xdpi, &ydpi)) return NULL;
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
if (!desc) return NULL;
float scaled_point_sz = ((xdpi + ydpi) / 144.0) * point_sz;
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz, NULL);
CFRelease(desc); desc = NULL;
if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; }
const char *psname = convert_cfstring(CTFontCopyPostScriptName(font), 1);
NSURL *url = (NSURL*)CTFontCopyAttribute(font, kCTFontURLAttribute);
PyObject *path = PyUnicode_FromString([[url path] UTF8String]);
[url release];
if (path == NULL) { CFRelease(font); return NULL; }
PyObject *ans = ft_face_from_path_and_psname(path, psname, (void*)font, free_font, true, 3, point_sz, xdpi, ydpi, CTFontGetLeading(font));
Py_DECREF(path);
if (ans == NULL) { CFRelease(font); }
return ans;
}
// Boilerplate {{{
static PyMethodDef module_methods[] = {
METHODB(coretext_all_fonts, METH_NOARGS),
METHODB(face_for_text, METH_VARARGS),
METHODB(create_face, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};
int
init_CoreText(PyObject *module) {
if (PyModule_AddFunctions(module, module_methods) != 0) return 0;
return 1;
}
// }}}