751 lines
20 KiB
Plaintext
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
|
||
|
}
|