// pseudo-go func (f *CTFont) IsRegistered() bool { n := f.Attribute(kCTFontRegistrationScopeAttribute) if n == nil { return false } return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } // based on CoreGraphics dylib's _CGFontCopyName // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such const ( kCGFontNameKeyPostScriptName = xxx kCGFontNameKeyPreferredSubfamily = xxx kCGFontNameKeyFontSubfamily = xxx kCGFontNameKeyFullName = xxx kCGFontNameKeyPreferredFamily = xxx kCGFontNameKeyFontFamily = xxx ) func (f *CGFont) CopyName(key int) (string, bool) { // TODO } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const func RegistryDetermineOS2Weight(table *CFData) (float32, bool) { if table == nil { return 0, false } if table.Len() < 78 { return 0, false } b := table.Bytes() usWeightClass := uint16be(b[4:6]) if usWeightClass >= 10 { // do nothing; we are preserving the original asm comparisons } else { usWeightClass *= 100 } /* TODO: 000000000000b37e mov dx, word [rax+4] 000000000000b382 mov cx, dx 000000000000b385 rol cx, 0x8 000000000000b389 movzx esi, cx 000000000000b38c imul ecx, ecx, 100 000000000000b38f cmp esi, 10 000000000000b392 cmovae cx, si 000000000000b396 test dx, dx 000000000000b399 cmove cx, si what's the function of the last two instructions? */ // note that this is an unsigned comparison, so underflow will result in a number > 998 // the effect is the same as (usWeightClass == 0) || (usWeightClass >= 1000) if (usWeightClass - 1) > 998 { // note the - 2 here; the switch cases below reflect that! // also note that b[0x22] and panose will be unsigned, so underflow will result in a number > 9 panose := b[0x22] - 2 if panose > 9 { return 0, false } switch panose { case 0: return float32as(-0.500000, 0xbf000000), true case 1: return float32as(-0.400000, 0xbecccccd), true case 2: // yes, this returns false; I don't know why return float32as(-0.300000, 0xbe99999a), false case 3: return float32as(-0.230000, 0xbe6b851f), true case 4: return float32as(0.230000, 0x3e6b851f), true case 5: return float32as(0.250000, 0x3e800000), true case 6: return float32as(0.400000, 0x3ecccccd), true case 7: return float32as(0.560000, 0x3f0f5c29), true case 8: return float32as(0.620000, 0x3f1eb852), true case 9: return float32as(0.800000, 0x3f4ccccd), true } // should not reach here } // let's mimic the assembly here too // the gotos avoid the massive if nesting // also note I'm using Go idioms and not saying "else return", imagine those if you must if usWeightClass > 100 { if usWeightClass > 200 { goto do201AndUp } return float32as(-0.500000, 0xbf000000), true } return float32as(-0.800000, 0xbf4ccccd), true do201AndUp: if usWeightClass > 300 { if usWeightClass > 400 { goto do401AndUp } return float32as(0.000000, 0x0), true } return float32as(-0.400000, 0xbecccccd), true do401AndUp: if usWeightClass > 500 { if usWeightClass > 600 { goto do601AndUp } return float32as(0.250000, 0x3e800000), true } return float32as(0.230000, 0x3e6b851), true do601AndUp: if usWeightClass > 700 { if usWeightClass > 800 { goto do801AndUp } return float32as(0.500000, 0x3f000000), true } return float32as(0.400000, 0x3ecccccd), true do801AndUp: if usWeightClass > 900 { if usWeightClass > 950 { return float32(0.800000, 0x3f4ccccd), true } return float32(0.750000, 0x3f400000), true } return float32as(0.620000, 0x3f1eb852), true } // based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) func (f *Font) WeightFromFontRegistry32() float32 { var weight float32 var hasWeight bool = false cgfont := f.CGFont() if f.RegistryHasMetadata() { wv := f.RegistryMetadataValueForKey("MTD_Typeface_Weight_VisualDescriptor") if wv != nil { if wn, ok := wv.(string); ok { // note: uses CFStringCompare(0) switch wn { case "reg": weight = float32as(0.000000, 0x0) hasWeight = true case "semi": weight = float32as(0.300000, 0x3e99999a) hasWeight = true case "bold": weight = float32as(0.400000, 0x3ecccccd) hasWeight = true case "light": weight = float32as(-0.400000, 0xbecccccd) hasWeight = true case "med": weight = float32as(0.230000, 0x3e6b851f) hasWeight = true case "heavy": weight = float32as(0.560000, 0x3f0f5c29) hasWeight = true case "black": weight = float32as(0.620000, 0x3f1eb852) hasWeight = true case "thin": weight = float32as(-0.600000, 0xbf19999a) hasWeight = true case "ulight": weight = float32as(-0.800000, 0xbf4ccccd) hasWeight = true } } } } cgpsname, ok := cgfont.CopyName(kCGFontNameKeyPostScriptName) if ok { // note: uses CFStringCompare(0) switch cgpsname { case "LucidaGrande", ".LucidaGrandeUI", ".Keyboard": weight = float32as(0.000000, 0x0) hasWeight = true case "STHeiti": weight = float32as(0.240000, 0x3e75c28f) hasWeight = true case "STXihei": weight = float32as(-0.100000, 0xbdcccccd) hasWeight = true case "TimesNewRomanPSMT": weight = float32as(0.000000, 0x0) hasWeight = true } } styleGlossaryStrings := []int{ kCGFontNameKeyPreferredSubfamily, kCGFontNameKeyFontSubfamily, kCGFontNameKeyFullName, kCGFontNameKeyPreferredFamily, kCGFontNameKeyFontFamily, } weightNameMap := []struct { key string val float32 }{ { "Ultra Light", float32as(-0.800000f, 0xbf4ccccd) }, { "Ultra Black", float32as(0.750000f, 0x3f400000) }, { "Extra Light", float32as(-0.500000f, 0xbf000000) }, { "UltraBlack", float32as(0.750000f, 0x3f400000) }, { "ExtraBlack", float32as(0.800000f, 0x3f4ccccd) }, { "UltraLight", float32as(-0.800000f, 0xbf4ccccd) }, { "ExtraLight", float32as(-0.500000f, 0xbf000000) }, { "Ultra Thin", float32as(-0.800000f, 0xbf4ccccd) }, { "Extra Thin", float32as(-0.800000f, 0xbf4ccccd) }, { "Heavy Face", float32as(0.560000f, 0x3f0f5c29) }, { "Semi Light", float32as(-0.200000f, 0xbe4ccccd) }, { "Extra Bold", float32as(0.500000f, 0x3f000000) }, { "Ultra Bold", float32as(0.700000f, 0x3f333333) }, { "HeavyFace", float32as(0.560000f, 0x3f0f5c29) }, { "ExtraBold", float32as(0.500000f, 0x3f000000) }, { "UltraBold", float32as(0.700000f, 0x3f333333) }, { "Ext Black", float32as(0.800000f, 0x3f4ccccd) }, { "SemiLight", float32as(-0.200000f, 0xbe4ccccd) }, { "Demi Bold", float32as(0.250000f, 0x3e800000) }, { "Semi Bold", float32as(0.300000f, 0x3e99999a) }, { "Ext Light", float32as(-0.500000f, 0xbf000000) }, { "Ext Bold", float32as(0.500000f, 0x3f000000) }, { "DemiBold", float32as(0.250000f, 0x3e800000) }, { "SemiBold", float32as(0.300000f, 0x3e99999a) }, { "HairLine", float32as(-0.800000f, 0xbf4ccccd) }, { "Ext Thin", float32as(-0.800000f, 0xbf4ccccd) }, { "Medium", float32as(0.230000f, 0x3e6b851f) }, { "Poster", float32as(0.800000f, 0x3f4ccccd) }, { "Light", float32as(-0.400000f, 0xbecccccd) }, { "Ultra", float32as(0.500000f, 0x3f000000) }, { "Heavy", float32as(0.560000f, 0x3f0f5c29) }, { "Extra", float32as(0.500000f, 0x3f000000) }, { "Black", float32as(0.620000f, 0x3f1eb852) }, { "Super", float32as(0.620000f, 0x3f1eb852) }, { "Obese", float32as(0.850000f, 0x3f59999a) }, { "Lite", float32as(-0.400000f, 0xbecccccd) }, { "Book", float32as(-0.230000f, 0xbe6b851f) }, { "Demi", float32as(0.250000f, 0x3e800000) }, { "Semi", float32as(0.300000f, 0x3e99999a) }, { "Thin", float32as(-0.500000f, 0xbf000000) }, { "Bold", float32as(0.400000f, 0x3ecccccd) }, { "Nord", float32as(0.800000f, 0x3f4ccccd) }, { "Fat", float32as(0.750000f, 0x3f400000) }, { "W1", float32as(-0.230000f, 0xbe6b851f) }, { "W2", float32as(-0.500000f, 0xbf000000) }, { "W3", float32as(-0.230000f, 0xbe6b851f) }, { "W4", float32as(0.000000f, 0x0) }, { "W5", float32as(0.230000f, 0x3e6b851f) }, { "W6", float32as(0.300000f, 0x3e99999a) }, { "W7", float32as(0.440000f, 0x3ee147ae) }, { "W8", float32as(0.540000f, 0x3f0a3d71) }, { "W9", float32as(0.620000f, 0x3f1eb852) }, } for _, key := range styleGlossaryStrings { if hasWeight { break } str, ok := cgfont.CopyName(key) if !ok { continue } for _, m := range weightNameMap { if str.FindWithOptions(m.key, CFRangeMake(0, str.CFStringLength()), kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral, nil) { weight = m.val hasWeight = true break } } } if !hasWeight { os2table := cgfont.TableForTag('OS/2') weight, hasWeight = RegistryDetermineOS2Weight(os2table) } if !hasWeight { headtable := cgfont.TableForTag('head') if headtable != nil { if headtable.Len() >= 54 { b := headtable.Bytes() if (b[0x2d] & 1) != 0 { weight = float32as(0.400000, 0x3ecccccd) hasWeight = true } } } } styleGlossaryAbbreviationKeys := []int{ kCGFontNameKeyPreferredSubfamily, kCGFontNameKeyFontSubfamily, } abbreviatedWeightNameMap := []struct { key string val float32 }{ { "EL", float32as(-0.200000, 0xbe4ccccd) }, { "EB", float32as(0.500000, 0x3f000000) }, { "SB", float32as(0.300000, 0x3e99999a) }, { "UH", float32as(0.800000, 0x3f4ccccd) }, { "U", float32as(0.700000, 0x3f333333) }, { "L", float32as(-0.400000, 0xbecccccd) }, { "H", float32as(0.560000, 0x3f0f5c29) }, { "B", float32as(0.400000, 0x3ecccccd) }, { "M", float32as(0.230000, 0x3e6b851f) }, { "R", float32as(0.000000, 0x0) }, } if !hasWeight { for _, key := range styleGlossaryAbbreviationStrings { str, ok := cgfont.CopyName(key) if !ok { continue } for _, m := range abbreviatedWeightNameMap { if str.Compare(m.key, kCFCompareCaseInsensitive) == kCFCompareEqualTo { weight = m.val hasWeight = true break } } if hasWeight { break } } } if !hasWeight { return float32as(0.000000, 0x0) } return weight } // because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done func (f *Font) WeightFromFontRegistry() float64 { return CFNumberWithFloat32(f.WeightFromFontRegistry32()).Float64Value() } // based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short) func CoreText_WeightOfClass(usWeightClass uint16) float64 { if usWeightClass >= 11 { // do nothing; we are preserving the original asm comparisons // and yes, this one is 11, but the one above is 10 } else { usWeightClass *= 100 } // figure out what two floats our weight will be between i := usWeightClass / 100 j := i + 1 if j > 10 { j = 10 } b := float64(i * 100) c := float64(j * 100) a := float64(0) if b != c { a = float64(usWeightClass) a -= b c -= b a /= c } scales := []float32{ float32as(-1.000000, 0xbf800000), float32as(-0.700000, 0xbf333333), float32as(-0.500000, 0xbf000000), float32as(-0.230000, 0xbe6b851f), float32as(0.000000, 0x0), float32as(0.200000, 0x3e4ccccd), float32as(0.300000, 0x3e99999a), float32as(0.400000, 0x3ecccccd), float32as(0.600000, 0x3f19999a), float32as(0.800000, 0x3f4ccccd), float32as(1.000000, 0x3f800000), } c = float64(scale[i]) b = float64[scale[j]) return fma(a, b, c) } // based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*) func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) { str.Fold(kCFCompareCaseInsensitive, nil) weightNameMap := []struct { key string val float32 }{ { "ultra light", float32as(-0.800000, 0xbf4ccccd) }, { "ultra black", float32as(0.750000, 0x3f400000) }, { "extra light", float32as(-0.500000, 0xbf000000) }, { "ultralight", float32as(-0.800000, 0xbf4ccccd) }, { "ultrablack", float32as(0.750000, 0x3f400000) }, { "extrablack", float32as(0.800000, 0x3f4ccccd) }, { "extralight", float32as(-0.500000, 0xbf000000) } { "heavy face", float32as(0.560000, 0x3f0f5c29) }, { "semi light", float32as(-0.200000, 0xbe4ccccd) }, { "extra bold", float32as(0.500000, 0x3f000000) }, { "ultra bold", float32as(0.700000, 0x3f333333) }, { "heavyface", float32as(0.560000, 0x3f0f5c29) }, { "extrabold", float32as(0.500000, 0x3f000000) }, { "ultrabold", float32as(0.700000, 0x3f333333) }, { "semilight", float32as(-0.200000, 0xbe4ccccd) }, { "demi bold", float32as(0.250000, 0x3e800000) }, { "semi bold", float32as(0.300000, 0x3e99999a) }, { "demibold", float32as(0.250000, 0x3e800000) }, { "semibold", float32as(0.300000, 0x3e99999a) }, { "hairline", float32as(-0.700000, 0xbf333333) }, { "medium", float32as(0.230000, 0x3e6b851f) }, { "poster", float32as(0.800000, 0x3f4ccccd) }, { "light", float32as(-0.400000, 0xbecccccd) }, { "heavy", float32as(0.560000, 0x3f0f5c29) }, { "extra", float32as(0.500000, 0x3f000000) }, { "black", float32as(0.620000, 0x3f1eb852) }, { "super", float32as(0.620000, 0x3f1eb852) }, { "obese", float32as(0.850000, 0x3f59999a) }, { "lite", float32as(-0.400000, 0xbecccccd) }, { "book", float32as(-0.230000, 0xbe6b851f) }, { "demi", float32as(0.250000, 0x3e800000) }, { "semi", float32as(0.300000, 0x3e99999a) }, { "thin", float32as(-0.500000, 0xbf000000) }, { "bold", float32as(0.400000, 0x3ecccccd) }, { "nord", float32as(0.800000, 0x3f4ccccd) }, { "fat", float32as(0.750000, 0x3f400000) }, { "w1", float32as(-0.700000, 0xbf333333) }, { "w2", float32as(-0.500000, 0xbf000000) }, { "w3", float32as(-0.230000, 0xbe6b851f) }, { "w4", float32as(0.000000, 0x0) }, { "w5", float32as(0.230000, 0x3e6b851f) }, { "w6", float32as(0.300000, 0x3e99999a) }, { "w7", float32as(0.440000, 0x3ee147ae) }, { "w8", float32as(0.540000, 0x3f0a3d71) }, { "w9", float32as(0.620000, 0x3f1eb852) }, } for _, m := range weightNameMap { if strstr(str, m.key) != nil { val := CFNumberWithFloat32(m.val) return val.Float64Value(), true } } return 0, false } // based on CoreText dylib's __ZNK9TBaseFont29CreateTraitsValuesPerFontInfoEP12MetadataFlag — TBaseFont::CreateTraitsValuesPerFontInfo(MetadataFlag*) const func (f *CTFont) Weight() float64 { if f.IsRegistered() { return f.WeightFromFontRegistry() } weight := float64as(2.0, 0x4000000000000000) ebx := -1 hasWeight := false name := f.Name(kCTFontPostScriptNameKey) if name != nil { switch *name { case "LucidaGrande": weight = float64as(0.000000, 0x0) hasWeight = true case ".LucidaGrandeUI": weight = float64as(0.000000, 0x0) hasWeight = true case "STHeiti": weight = float64as(0.240000, 0x3fceb851eb851eb8) hasWeight = true case "STXihei": weight = float64as(-0.100000, 0xbfb999999999999a) hasWeight = true case "TimesNewRomanPSMT": weight = float64as(0.000000, 0x0) hasWeight = true // there is one more hardcoded case, for "Times-Roman", but that will only set the class style, not the weight } } os2table := f.Table('OS/2') if os2table != nil { if !hasWeight { var usWeightClass uint16 valid := false if os2table.Len() > 77 { b := os2table.Bytes() usWeightClass = uint16be(b[4:6]) if usWeightClass > 1000 { weight = 0 hasWeight = false } else { valid = true } } else { usWeightClass = 0 valid = true } if valid { weight = CoreText_WeightOfClass(usWeightClass) hasWeight = true } } } styleGlossaryNames := []string{ kCTFontSubFamilyNameKey, kCTFontFullNameKey, kCTFontFamilyNameKey, } for _, key := range styleGlossaryNames { name := f.Name(key) if name == nil { continue } candidate, ok := CoreText_WeightByStyleGlossaryString(*name) if !ok { continue } if !hasWeight { weight = candidate hasWeight = true } } if hasWeight { return weight } return 0 } func (f *Font) ShouldEnableBoldSymbolicTrait() bool { if f.IsRegistered() { return f.ShouldEnableBoldSymbolicTraitFromRegistry() } no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64) return !no }