And finally finished the core Core Text weight determination functionality pseudo-Go-code. God damn. And I have a funny feeling stretches are going to be a tad bit more inconsistent too... :| Not quite complete, though; need to fill in the name table parsing rules first.
This commit is contained in:
parent
683bd47491
commit
f94de2eef8
|
@ -13,8 +13,125 @@ func (f *CTFont) IsRegistered() bool {
|
|||
// 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&)
|
||||
|
@ -81,6 +198,148 @@ func (f *Font) WeightFromFontRegistry32() float32 {
|
|||
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
|
||||
|
@ -92,6 +351,7 @@ func (f *Font) WeightFromFontRegistry() float64 {
|
|||
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
|
||||
}
|
||||
|
@ -133,8 +393,7 @@ func CoreText_WeightOfClass(usWeightClass uint16) float64 {
|
|||
// based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*)
|
||||
func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) {
|
||||
str.Fold(kCFCompareCaseInsensitive, nil)
|
||||
|
||||
var weightNameMap = []struct {
|
||||
weightNameMap := []struct {
|
||||
key string
|
||||
val float32
|
||||
}{
|
||||
|
|
Loading…
Reference in New Issue