libui/doc/export/ctweights

751 lines
20 KiB
Plaintext

// pseudo-go
func (f *CTFont) IsRegistered() bool {
n := f.Attribute(kCTFontRegistrationScopeAttribute)
if n == nil {
return false
}
return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone
}
// this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there
type x_list struct {
Data interface{}
Next *x_list
}
func (x *x_list) Prepend(data interface{}) *x_list {
y := malloc(sizeof (x_list))
if y != nil {
y.data = data
y.next = x
return y
}
return x
}
func (x *x_list) Reverse() *x_list {
if x == nil {
return nil
}
var old, next *x_list
next = nil
for {
old = x
x = old.next
old.next = next
next = old
if x == nil {
break
}
}
return old
}
func (x *x_list) Concat(y *x_list) *x_list {
if x == nil {
return y
}
start := x
z := x
for {
x = z
z = z.next
if z == nil {
break
}
}
x.next = y
return start
}
// 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 = 0x6
kCGFontNameKeyPreferredSubfamily = 0x11
kCGFontNameKeyFontSubfamily = 0x2
kCGFontNameKeyFullName = 0x4
kCGFontNameKeyPreferredFamily = 0x10
kCGFontNameKeyFontFamily = 0x1
)
func (f *CGFont) CopyName(key int) (string, bool) {
table := f.TableForTag('name')
b := table.Bytes()
n := table.Len()
// this code looks weird, but we're imitating the assembly, or the effective effects thereof
offCount := uint16(0)
offStringOffset := uint16(2)
if n > 1 {
offCount = 2
offStringOffset = 4
}
count := uint16(0)
if int(offCount) <= n {
count = uint16be(b[offCount:offCount + 2])
}
offNameRecord := offStringOffset + 2
stringOffset := uint16(0)
if int(offNameRecord) <= n {
stringOffset = uint16be(b[offStringOffset:offStringOffset + 2])
}
type NameRecord struct {
PlatformID uint16
PlatformSpecificID uint16
LanguageID uint16
NameID uint16
Length uint16
Offset uint16
}
var nameList *x_list
addrStrings := offNameRecords + (12 * count)
if addrStrings != stringOffset {
goto hasLanguageTags
}
pos := offNameRecords
if count == 0 {
// TODO note assembly logic here
} else {
for {
var nr NameRecord
nr.PlatformID = 0
next := pos + 2
if int(next) <= n {
nr.PlatformID = uint16be(b[pos:pos + 2])
pos = next
}
nr.PlatformSpecificID = 0
next = pos + 2
if int(next) <= n {
nr.PlatformSpecificID = uint16be(b[pos:pos + 2])
pos = next
}
nr.LanguageID = 0
next = pos + 2
if int(next) <= n {
nr.LanguageID = uint16be(b[pos:pos + 2])
pos = next
}
nr.NameID = 0
next = pos + 2
if int(next) <= n {
nr.NameID = uint16be(b[pos:pos + 2])
pos = next
}
nr.Length = 0
next = pos + 2
if int(next) <= n {
nr.Length = uint16be(b[pos:pos + 2])
pos = next
}
nr.Offset = 0
next = pos + 2
if int(next) <= n {
nr.Offset = uint16be(b[pos:pos + 2])
pos = next
}
strpos := stringOffset + nr.Offset
if strpos >= n {
// TODO put comment about imitating the assembly comparisons here
} else {
realLen := nr.Length
strend = strpos + nr.Length
if strend > n {
realLen = nr.Length - strpos
strend = strpos + realLen
}
b := malloc(12 + realLen + 1)
if b != nil {
name := (*sfnt_name_t)(b)
name.PlatformID = nr.PlatformID
name.PlatformSpecificID = nr.PlatformSpecificID
name.LanguageID = nr.LanguageID
name.NameID = nr.NameID
name.Length = realLen
memcpy(&(name.Name), b[strpos:strend], realLen)
name.Name[realLen] = 0
nameList = nameList.Prepend(name)
}
}
count--
if count == 0 {
break
}
}
}
nameList = nameList.Reverse()
hasLanguageTags:
add_localized_names := func(platformID uint16, platformSpecificID uint16, to *x_list) *x_list {
out := (*x_list)(nil)
if nameList == nil {
xx TODO logic verbatim etc.
} else {
x := nameList
for {
name := (*sfnt_name_t)(x.data)
if name.PlatformID != platformID {
xx TODO
} else {
if platformSpecificID == 0xFFFF || name.PlatformSpecificID == platformSpecificID {
out = out.Prepend(name)
}
}
x = x.next
if x == nil {
break
}
}
}
out = out.Reverse()
return to.Concat(out)
}
localized := (*x_list)(nil)
localized = add_localized_names(0x1, 0xFFFF, localized)
localized = add_localized_names(0, 0xFFFF, localized)
localized = add_localized_names(0x3, 0xFFFF, localized)
localized = add_localized_names(0x1, 0, localized)
localized = add_localized_names(0x3, 0x9, localized)
localized = add_localized_names(0x3, 0x409, localized)
sysLocale := CFLocaleGetSystem()
}
// 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, 0x3e6b851f), 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
}