package harfbuzz
import (
"github.com/go-text/typesetting/font"
"github.com/go-text/typesetting/font/opentype/tables"
)
type Face = *font .Face
type Font struct {
face Face
gsubAccels, gposAccels []otLayoutLookupAccelerator
morxAccels [][]morxSubtableAccelerator
kernAccels, kerxAccels []kernxSubtableAccelerator
faceUpem int32
Ptem float32
XScale, YScale int32
slant float32
xEmbolden float32
}
func NewFont (face Face ) *Font {
var font Font
font .face = face
font .faceUpem = Position (font .face .Upem ())
font .XScale = font .faceUpem
font .YScale = font .faceUpem
font .gsubAccels = make ([]otLayoutLookupAccelerator , len (face .GSUB .Lookups ))
for i , l := range face .GSUB .Lookups {
font .gsubAccels [i ].init (lookupGSUB (l ))
}
font .gposAccels = make ([]otLayoutLookupAccelerator , len (face .GPOS .Lookups ))
for i , l := range face .GPOS .Lookups {
font .gposAccels [i ].init (lookupGPOS (l ))
}
font .morxAccels = make ([][]morxSubtableAccelerator , len (face .Morx ))
for i , chain := range face .Morx {
font .morxAccels [i ] = newMorxChainAccelerator (chain )
}
font .kernAccels = make ([]kernxSubtableAccelerator , len (face .Kern ))
for i , subtable := range face .Kern {
font .kernAccels [i ] = newKernxSubtableAccelerator (subtable )
}
font .kerxAccels = make ([]kernxSubtableAccelerator , len (face .Kerx ))
for i , subtable := range face .Kerx {
font .kerxAccels [i ] = newKernxSubtableAccelerator (subtable )
}
return &font
}
func (f *Font ) SetVarCoordsDesign (coords []float32 ) {
f .face .SetCoords (f .face .NormalizeVariations (coords ))
}
func (f *Font ) Face () Face { return f .face }
func (f *Font ) nominalGlyph (r rune , notFound GID ) (GID , bool ) {
g , ok := f .face .NominalGlyph (r )
if !ok {
g = notFound
}
return g , ok
}
func (f *Font ) xStrength () Position { return roundf (float32 (f .XScale ) * f .xEmbolden ) }
func (f *Font ) emScaleX (v int16 ) Position { return Position (v ) * f .XScale / f .faceUpem }
func (f *Font ) emScaleY (v int16 ) Position { return Position (v ) * f .YScale / f .faceUpem }
func (f *Font ) emScalefX (v float32 ) Position { return emScalef (v , f .XScale , f .faceUpem ) }
func (f *Font ) emScalefY (v float32 ) Position { return emScalef (v , f .YScale , f .faceUpem ) }
func (f *Font ) emFscaleX (v int16 ) float32 { return emFscale (v , f .XScale , f .faceUpem ) }
func (f *Font ) emFscaleY (v int16 ) float32 { return emFscale (v , f .YScale , f .faceUpem ) }
func emScalef(v float32 , scale , faceUpem int32 ) Position {
return roundf (v * float32 (scale ) / float32 (faceUpem ))
}
func emFscale(v int16 , scale , faceUpem int32 ) float32 {
return float32 (v ) * float32 (scale ) / float32 (faceUpem )
}
type GlyphExtents struct {
XBearing int32
YBearing int32
Width int32
Height int32
}
func (f *Font ) GlyphExtents (glyph GID ) (out GlyphExtents , ok bool ) {
ext , ok := f .face .GlyphExtents (glyph )
if !ok {
return out , false
}
syntheticGlyphExtents (&ext , f .slant , f .xEmbolden )
out .XBearing = f .emScalefX (ext .XBearing )
out .Width = f .emScalefX (ext .Width )
out .YBearing = f .emScalefY (ext .YBearing )
out .Height = f .emScalefY (ext .Height )
return out , true
}
func (f *Font ) GlyphAdvanceForDirection (glyph GID , dir Direction ) (x , y Position ) {
if dir .isHorizontal () {
return f .GlyphHAdvance (glyph ), 0
}
return 0 , f .getGlyphVAdvance (glyph )
}
func (f *Font ) GlyphHAdvance (glyph GID ) Position {
adv := f .face .HorizontalAdvance (glyph )
out := f .emScalefX (adv )
if f .xEmbolden != 0 && out != 0 {
out += f .xStrength ()
}
return out
}
func (f *Font ) getGlyphVAdvance (glyph GID ) Position {
if f .face .HasVerticalMetrics () {
adv := f .face .VerticalAdvance (glyph )
return f .emScalefY (adv )
} else {
fontExtents := f .fontHExtentsWithFallback ()
advance := Position (-(fontExtents .Ascender - fontExtents .Descender ))
return advance
}
}
func (f *Font ) subtractGlyphOriginForDirection (glyph GID , direction Direction ,
x , y Position ,
) (Position , Position ) {
originX , originY := f .getGlyphOriginForDirection (glyph , direction )
return x - originX , y - originY
}
func (f *Font ) getGlyphOriginForDirection (glyph GID , direction Direction ) (x , y Position ) {
if direction .isHorizontal () {
return f .getGlyphHOriginWithFallback (glyph )
}
return f .getGlyphVOriginWithFallback (glyph )
}
func (f *Font ) getGlyphHOriginWithFallback (glyph GID ) (Position , Position ) {
x , y , ok := f .face .GlyphHOrigin (glyph )
if !ok {
xf , yf := f .face .GlyphVOrigin (glyph )
x , y := f .emScalefX (xf ), f .emScalefY (yf )
dx , dy := f .guessVOriginMinusHOrigin (glyph )
return x - dx , y - dy
}
x_ , y_ := f .emScalefX (float32 (x )), f .emScalefY (float32 (y ))
if f .xEmbolden != 0 {
strength := f .xStrength ()
x_ += strength
y_ += strength
}
return x_ , y_
}
func (f *Font ) getGlyphVOriginWithFallback (glyph GID ) (Position , Position ) {
x , y := f .face .GlyphVOrigin (glyph )
x_ , y_ := f .emScalefX (2 *x )/2 , f .emScalefY (y )
if f .xEmbolden != 0 {
strength := f .xStrength ()
x_ += strength
y_ += strength
}
return x_ , y_
}
func (f *Font ) guessVOriginMinusHOrigin (glyph GID ) (x , y Position ) {
x = f .GlyphHAdvance (glyph ) / 2
y = f .getHExtendsAscender ()
return x , y
}
func (f *Font ) getHExtendsAscender () Position {
extents , ok := f .face .FontHExtents ()
if !ok {
return f .YScale * 4 / 5
}
return f .emScalefY (extents .Ascender )
}
func (f *Font ) hasGlyph (ch rune ) bool {
_ , ok := f .face .NominalGlyph (ch )
return ok
}
func (f *Font ) subtractGlyphHOrigin (glyph GID , x , y Position ) (Position , Position ) {
originX , originY := f .getGlyphHOriginWithFallback (glyph )
return x - originX , y - originY
}
func (f *Font ) subtractGlyphVOrigin (glyph GID , x , y Position ) (Position , Position ) {
originX , originY := f .getGlyphVOriginWithFallback (glyph )
return x - originX , y - originY
}
func (f *Font ) addGlyphHOrigin (glyph GID , x , y Position ) (Position , Position ) {
originX , originY := f .getGlyphHOriginWithFallback (glyph )
return x + originX , y + originY
}
func (f *Font ) getGlyphContourPointForOrigin (glyph GID , pointIndex uint16 , direction Direction ) (x , y Position , ok bool ) {
x , y , ok = f .face .GetGlyphContourPoint (glyph , pointIndex )
if ok {
x , y = f .subtractGlyphOriginForDirection (glyph , direction , x , y )
}
return x , y , ok
}
func (f *Font ) fontHExtentsWithFallback () font .FontExtents {
extents , ok := f .face .FontHExtents ()
extents .Ascender = float32 (f .emScalefY (extents .Ascender ))
extents .Descender = float32 (f .emScalefY (extents .Descender ))
extents .LineGap = float32 (f .emScalefY (extents .LineGap ))
if !ok {
extents .Ascender = float32 (f .YScale ) * 0.8
extents .Descender = extents .Ascender - float32 (f .YScale )
extents .LineGap = 0
}
return extents
}
func (f *Font ) ExtentsForDirection (direction Direction ) font .FontExtents {
var (
extents font .FontExtents
ok bool
)
if direction .isHorizontal () {
return f .fontHExtentsWithFallback ()
} else {
extents , ok = f .face .FontVExtents ()
extents .Ascender = float32 (f .emScalefX (extents .Ascender ))
extents .Descender = float32 (f .emScalefX (extents .Descender ))
extents .LineGap = float32 (f .emScalefX (extents .LineGap ))
if !ok {
extents .Ascender = float32 (f .XScale ) * 0.5
extents .Descender = extents .Ascender - float32 (f .XScale )
extents .LineGap = 0
}
xStrength := float32 (f .XScale ) * f .xEmbolden
extents .Ascender += xStrength
}
return extents
}
func (font *Font ) varCoords () []tables .Coord { return font .face .Coords () }
func (font *Font ) getXDelta (varStore tables .ItemVarStore , device tables .DeviceTable ) Position {
switch device := device .(type ) {
case tables .DeviceHinting :
xPpem , _ := font .face .Ppem ()
return device .GetDelta (xPpem , font .XScale )
case tables .DeviceVariation :
return font .emScalefX (varStore .GetDelta (tables .VariationStoreIndex (device ), font .varCoords ()))
default :
return 0
}
}
func (font *Font ) getYDelta (varStore tables .ItemVarStore , device tables .DeviceTable ) Position {
switch device := device .(type ) {
case tables .DeviceHinting :
_ , yPpem := font .face .Ppem ()
return device .GetDelta (yPpem , font .YScale )
case tables .DeviceVariation :
return font .emScalefY (varStore .GetDelta (tables .VariationStoreIndex (device ), font .varCoords ()))
default :
return 0
}
}
func syntheticGlyphExtents(extents *font .GlyphExtents , slant , xEmbolden float32 ) {
if slant != 0 {
x1 := extents .XBearing
y1 := extents .YBearing
x2 := extents .XBearing + extents .Width
y2 := extents .YBearing + extents .Height
x1 += minF (y1 *slant , y2 *slant )
x2 += maxF (y1 *slant , y2 *slant )
extents .XBearing = x1
extents .Width = x2 - extents .XBearing
}
if xEmbolden != 0 {
yShift := xEmbolden
extents .YBearing += yShift
extents .Height -= yShift
xShift := xEmbolden
extents .Width += xShift
}
}
func (f *Font ) GetOTLigatureCarets (direction Direction , glyph GID ) []Position {
varStore := f .face .GDEF .ItemVarStore
list := f .face .GDEF .LigCaretList
if list .Coverage == nil {
return nil
}
index , ok := list .Coverage .Index (gID (glyph ))
if !ok {
return nil
}
glyphCarets := list .LigGlyphs [index ].CaretValues
out := make ([]Position , len (glyphCarets ))
for i , c := range glyphCarets {
out [i ] = f .getCaretValue (c , direction , glyph , varStore )
}
return out
}
func (f *Font ) getCaretValue (caret tables .CaretValue , direction Direction , glyph GID , varStore tables .ItemVarStore ) Position {
switch caret := caret .(type ) {
case tables .CaretValue1 :
if direction .isHorizontal () {
return f .emScaleX (int16 (caret .Coordinate ))
} else {
return f .emScaleY (int16 (caret .Coordinate ))
}
case tables .CaretValue2 :
x , y , _ := f .getGlyphContourPointForOrigin (glyph , uint16 (caret .CaretValuePointIndex ), direction )
if direction .isHorizontal () {
return x
} else {
return y
}
case tables .CaretValue3 :
if direction .isHorizontal () {
return f .emScaleX (caret .Coordinate ) + f .getXDelta (varStore , caret .Device )
} else {
return f .emScaleY (caret .Coordinate ) + f .getYDelta (varStore , caret .Device )
}
default :
return 0
}
}
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 .