113 lines
3.3 KiB
Mathematica
113 lines
3.3 KiB
Mathematica
|
// note: this doesn't work for languages; we have to parse the ltag table
|
||
|
|
||
|
// fortunately features that aren't supported are simply ignored, so we can copy them all in
|
||
|
// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore
|
||
|
// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly!
|
||
|
CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language)
|
||
|
{
|
||
|
CTFontDescriptorRef new;
|
||
|
CFMutableArrayRef outerArray;
|
||
|
CFDictionaryRef innerDict;
|
||
|
CFNumberRef numType, numSelector;
|
||
|
const void *keys[2], *values[2];
|
||
|
size_t i;
|
||
|
CFArrayRef languages;
|
||
|
CFIndex il, nl;
|
||
|
CFStringRef curlang;
|
||
|
char d[2];
|
||
|
|
||
|
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||
|
if (outerArray == NULL) {
|
||
|
// TODO
|
||
|
}
|
||
|
keys[0] = kCTFontFeatureTypeIdentifierKey;
|
||
|
keys[1] = kCTFontFeatureSelectorIdentifierKey;
|
||
|
for (i = 0; i < n; i++) {
|
||
|
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||
|
(const SInt16 *) (types + i));
|
||
|
numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||
|
(const SInt16 *) (selectors + i));
|
||
|
values[0] = numType;
|
||
|
values[1] = numSelector;
|
||
|
innerDict = CFDictionaryCreate(NULL,
|
||
|
keys, values, 2,
|
||
|
// TODO are these correct?
|
||
|
&kCFCopyStringDictionaryKeyCallBacks,
|
||
|
&kCFTypeDictionaryValueCallBacks);
|
||
|
if (innerDict == NULL) {
|
||
|
// TODO
|
||
|
}
|
||
|
CFArrayAppendValue(outerArray, innerDict);
|
||
|
CFRelease(innerDict);
|
||
|
CFRelease(numSelector);
|
||
|
CFRelease(numType);
|
||
|
}
|
||
|
|
||
|
// now we have to take care of the language
|
||
|
if (language != NULL) {
|
||
|
languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute);
|
||
|
if (languages != NULL) {
|
||
|
nl = CFArrayGetCount(languages);
|
||
|
d[0] = language[0];
|
||
|
if (d[0] >= 'A' && d[0] <= 'Z')
|
||
|
d[0] += 'a' - 'A';
|
||
|
d[1] = language[1];
|
||
|
if (d[1] >= 'A' && d[1] <= 'Z')
|
||
|
d[1] += 'a' - 'A';
|
||
|
for (il = 0; il < nl; il++) {
|
||
|
char c[2];
|
||
|
|
||
|
curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il);
|
||
|
// TODO check for failure
|
||
|
CFStringGetBytes(curlang, CFRangeMake(0, 2),
|
||
|
kCFStringEncodingUTF8, 0, false,
|
||
|
(UInt8 *) c, 2, NULL);
|
||
|
if (c[0] >= 'A' && c[0] <= 'Z')
|
||
|
c[0] += 'a' - 'A';
|
||
|
if (c[1] >= 'A' && c[1] <= 'Z')
|
||
|
c[1] += 'a' - 'A';
|
||
|
if (c[0] == d[0] && c[1] == d[1])
|
||
|
break;
|
||
|
}
|
||
|
if (il != nl) {
|
||
|
uint16_t typ;
|
||
|
|
||
|
typ = kLanguageTagType;
|
||
|
il++;
|
||
|
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||
|
(const SInt16 *) (&typ));
|
||
|
numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType,
|
||
|
&il);
|
||
|
values[0] = numType;
|
||
|
values[1] = numSelector;
|
||
|
innerDict = CFDictionaryCreate(NULL,
|
||
|
keys, values, 2,
|
||
|
// TODO are these correct?
|
||
|
&kCFCopyStringDictionaryKeyCallBacks,
|
||
|
&kCFTypeDictionaryValueCallBacks);
|
||
|
if (innerDict == NULL) {
|
||
|
// TODO
|
||
|
}
|
||
|
CFArrayAppendValue(outerArray, innerDict);
|
||
|
CFRelease(innerDict);
|
||
|
CFRelease(numSelector);
|
||
|
CFRelease(numType);
|
||
|
}
|
||
|
CFRelease(languages);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
keys[0] = kCTFontFeatureSettingsAttribute;
|
||
|
values[0] = outerArray;
|
||
|
innerDict = CFDictionaryCreate(NULL,
|
||
|
keys, values, 1,
|
||
|
// TODO are these correct?
|
||
|
&kCFCopyStringDictionaryKeyCallBacks,
|
||
|
&kCFTypeDictionaryValueCallBacks);
|
||
|
CFRelease(outerArray);
|
||
|
new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict);
|
||
|
CFRelease(desc);
|
||
|
CFRelease(innerDict);
|
||
|
return new;
|
||
|
}
|