package font
import (
"strings"
ot "github.com/go-text/typesetting/font/opentype"
"github.com/go-text/typesetting/font/opentype/tables"
)
var (
styleStrings [len (styleConsts )]string
weightStrings [len (weightConsts )]string
stretchStrings [len (stretchConsts )]string
)
func init() {
for i , v := range styleConsts {
styleStrings [i ] = v .name
}
for i , v := range weightConsts {
weightStrings [i ] = v .name
}
for i , v := range stretchConsts {
stretchStrings [i ] = v .name
}
}
var styleConsts = [...]struct {
name string
value Style
}{
{"italic" , StyleItalic },
{"kursiv" , StyleItalic },
{"oblique" , StyleItalic },
}
var weightConsts = [...]struct {
name string
value Weight
}{
{"thin" , WeightThin },
{"extralight" , WeightExtraLight },
{"ultralight" , WeightExtraLight },
{"light" , WeightLight },
{"demilight" , (WeightLight + WeightNormal ) / 2 },
{"semilight" , (WeightLight + WeightNormal ) / 2 },
{"book" , WeightNormal - 20 },
{"regular" , WeightNormal },
{"normal" , WeightNormal },
{"medium" , WeightMedium },
{"demibold" , WeightSemibold },
{"demi" , WeightSemibold },
{"semibold" , WeightSemibold },
{"extrabold" , WeightExtraBold },
{"superbold" , WeightExtraBold },
{"ultrabold" , WeightExtraBold },
{"bold" , WeightBold },
{"ultrablack" , WeightBlack + 20 },
{"superblack" , WeightBlack + 20 },
{"extrablack" , WeightBlack + 20 },
{"black" , WeightBlack },
{"heavy" , WeightBlack },
}
var stretchConsts = [...]struct {
name string
value Stretch
}{
{"ultracondensed" , StretchUltraCondensed },
{"extracondensed" , StretchExtraCondensed },
{"semicondensed" , StretchSemiCondensed },
{"condensed" , StretchCondensed },
{"normal" , StretchNormal },
{"semiexpanded" , StretchSemiExpanded },
{"extraexpanded" , StretchExtraExpanded },
{"ultraexpanded" , StretchUltraExpanded },
{"expanded" , StretchExpanded },
{"extended" , StretchExpanded },
}
type Style uint8
const (
StyleNormal Style = iota + 1
StyleItalic
)
type Weight float32
const (
WeightThin Weight = 100
WeightExtraLight Weight = 200
WeightLight Weight = 300
WeightNormal Weight = 400
WeightMedium Weight = 500
WeightSemibold Weight = 600
WeightBold Weight = 700
WeightExtraBold Weight = 800
WeightBlack Weight = 900
)
type Stretch float32
const (
StretchUltraCondensed Stretch = 0.5
StretchExtraCondensed Stretch = 0.625
StretchCondensed Stretch = 0.75
StretchSemiCondensed Stretch = 0.875
StretchNormal Stretch = 1.0
StretchSemiExpanded Stretch = 1.125
StretchExpanded Stretch = 1.25
StretchExtraExpanded Stretch = 1.5
StretchUltraExpanded Stretch = 2.0
)
type Aspect struct {
Style Style
Weight Weight
Stretch Stretch
}
func (fd *fontDescriptor ) aspect () Aspect {
out := fd .rawAspect ()
out .inferFromStyle (fd .additionalStyle ())
out .SetDefaults ()
return out
}
func (as *Aspect ) inferFromStyle (additionalStyle string ) {
additionalStyle = NormalizeFamily (additionalStyle )
if as .Style == 0 {
if index := stringContainsConst (additionalStyle , styleStrings [:]); index != -1 {
as .Style = styleConsts [index ].value
}
}
if as .Weight == 0 {
if index := stringContainsConst (additionalStyle , weightStrings [:]); index != -1 {
as .Weight = weightConsts [index ].value
}
}
if as .Stretch == 0 {
if index := stringContainsConst (additionalStyle , stretchStrings [:]); index != -1 {
as .Stretch = stretchConsts [index ].value
}
}
}
func (as *Aspect ) SetDefaults () {
if as .Style == 0 {
as .Style = StyleNormal
}
if as .Stretch == 0 {
as .Stretch = StretchNormal
}
if as .Weight == 0 {
as .Weight = WeightNormal
}
}
func (fd *fontDescriptor ) additionalStyle () string {
var style string
if fd .os2 != nil && fd .os2 .fsSelection &256 != 0 {
style = fd .names .Name (namePreferredSubfamily )
if style == "" {
style = fd .names .Name (nameFontSubfamily )
}
} else {
style = fd .names .Name (nameWWSSubfamily )
if style == "" {
style = fd .names .Name (namePreferredSubfamily )
}
if style == "" {
style = fd .names .Name (nameFontSubfamily )
}
}
style = strings .TrimSpace (style )
return style
}
func (fd *fontDescriptor ) rawAspect () Aspect {
var (
style Style
weight Weight
stretch Stretch
)
if fd .os2 != nil {
if fd .os2 .fsSelection &(1 <<9 ) != 0 || fd .os2 .fsSelection &1 != 0 {
style = StyleItalic
}
weight = Weight (fd .os2 .usWeightClass )
switch fd .os2 .usWidthClass {
case 1 :
stretch = StretchUltraCondensed
case 2 :
stretch = StretchExtraCondensed
case 3 :
stretch = StretchCondensed
case 4 :
stretch = StretchSemiCondensed
case 5 :
stretch = StretchNormal
case 6 :
stretch = StretchSemiExpanded
case 7 :
stretch = StretchExpanded
case 8 :
stretch = StretchExtraExpanded
case 9 :
stretch = StretchUltraExpanded
}
} else {
if isItalic := fd .head .MacStyle &2 != 0 ; isItalic {
style = StyleItalic
}
if isBold := fd .head .MacStyle &1 != 0 ; isBold {
weight = WeightBold
}
}
return Aspect {style , weight , stretch }
}
var rp = strings .NewReplacer (" " , "" , "\t" , "" )
func NormalizeFamily (family string ) string { return rp .Replace (strings .ToLower (family )) }
func stringContainsConst(str string , constants []string ) int {
for i , c := range constants {
if strings .Contains (str , c ) {
return i
}
}
return -1
}
const (
nameFontFamily tables .NameID = 1
nameFontSubfamily tables .NameID = 2
namePreferredFamily tables .NameID = 16
namePreferredSubfamily tables .NameID = 17
nameWWSFamily tables .NameID = 21
nameWWSSubfamily tables .NameID = 22
)
type os2Desc struct {
usWeightClass uint16
usWidthClass uint16
fsSelection uint16
}
func newOS2Desc(os tables .Os2 ) *os2Desc {
return &os2Desc {
usWeightClass : os .USWeightClass ,
usWidthClass : os .USWidthClass ,
fsSelection : os .FsSelection ,
}
}
type fontDescriptor struct {
os2 *os2Desc
names tables .Name
head tables .Head
}
func newFontDescriptor(ld *ot .Loader , buffer []byte ) (fontDescriptor , []byte ) {
var desc fontDescriptor
buffer , _ = ld .RawTableTo (ot .MustNewTag ("OS/2" ), buffer )
if os2 , _ , err := tables .ParseOs2 (buffer ); err == nil {
desc .os2 = newOS2Desc (os2 )
}
desc .head , buffer , _ = LoadHeadTable (ld , buffer )
buffer , _ = ld .RawTableTo (ot .MustNewTag ("name" ), buffer )
desc .names , _, _ = tables .ParseName (buffer )
return desc , buffer
}
func (fd *fontDescriptor ) family () string {
var family string
if fd .os2 != nil && fd .os2 .fsSelection &256 != 0 {
family = fd .names .Name (namePreferredFamily )
if family == "" {
family = fd .names .Name (nameFontFamily )
}
} else {
family = fd .names .Name (nameWWSFamily )
if family == "" {
family = fd .names .Name (namePreferredFamily )
}
if family == "" {
family = fd .names .Name (nameFontFamily )
}
}
return family
}
func max(a , b int ) int {
if a > b {
return a
}
return b
}
func abs(x int ) int {
if x < 0 {
return -x
}
return x
}
func approximatelyEqual(x , y int ) bool { return abs (x -y )*33 <= max (abs (x ), abs (y )) }
func (fd *Font ) IsMonospace () bool {
if fd .post .isFixedPitch {
return true
}
if fd .hmtx .IsEmpty () {
return false
}
if len (fd .hmtx .Metrics ) == 1 {
return true
}
var firstAdvance int
for gid , metric := range fd .hmtx .Metrics {
if gid == 0 {
continue
}
advance := int (metric .AdvanceWidth )
if advance == 0 {
continue
}
if firstAdvance == 0 {
firstAdvance = advance
continue
}
if approximatelyEqual (advance , firstAdvance ) {
continue
}
return false
}
return true
}
type Description struct {
Family string
Aspect Aspect
}
func Describe (ld *ot .Loader , buffer []byte ) (Description , []byte ) {
desc , buffer := newFontDescriptor (ld , buffer )
return Description {desc .family (), desc .aspect ()}, buffer
}
func (ft *Font ) Describe () Description {
desc := fontDescriptor {ft .os2 .os2Desc , ft .names , ft .head }
return Description {desc .family (), desc .aspect ()}
}
type STAT = tables .STAT
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .