diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 0a5dd8ac..b46872ce 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,8 @@ // 12 february 2017 #import "uipriv_darwin.h" +// LONGTERM FUTURE for typographic features, on 10.10 we can use OpenType tags directly! + // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm static NSColor *spellingColor = nil; @@ -70,8 +72,9 @@ struct foreachParams { struct fontParams { uiDrawFontDescriptor desc; uint16_t featureTypes[maxFeatures]; - uint16_t featureSpecifiers[maxFeatures]; + uint16_t featureSelectors[maxFeatures]; size_t nFeatures; + const char *language; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -239,18 +242,27 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; + // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc + case uiAttributeLanguage: + // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->language = (const char *) (spec->Value); + }); + break; // TODO } return 0; } -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +static CTFontRef fontdescToCTFont(struct fontParams *fp) { CTFontDescriptorRef desc; CTFontRef font; - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + desc = fontdescToCTFontDescriptor(&(fp->desc)); + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); + font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); CFRelease(desc); // TODO correct? return font; } @@ -263,7 +275,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) CFRange range; fp = (struct fontParams *) [val pointerValue]; - font = fontdescToCTFont(&(fp->desc)); + font = fontdescToCTFont(fp); range.location = [key integerValue]; range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); @@ -305,6 +317,7 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringRef base; CFMutableAttributedStringRef mas; struct foreachParams fep; + struct fontParams ffp; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { @@ -316,7 +329,9 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray if (defaultAttrs == NULL) { // TODO } - defaultCTFont = fontdescToCTFont(p->DefaultFont); + memset(&ffp, 0, sizeof (struct fontParams)); + ffp.desc = *(p->DefaultFont); + defaultCTFont = fontdescToCTFont(&ffp); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); ps = mkParagraphStyle(p); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index e1660c8b..184f1c6e 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -254,6 +254,115 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) stretchesToCTWidths[fd->Stretch]); } +// 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 + // TODO can we assume this is present? + if (language != NULL) { + languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); + 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; + 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; +} + // TODO deduplicate this from italicCloseness() static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) { diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 32ae8688..9d662cd5 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,6 +142,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m