Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/image/font/opentype: font metrics discrepancies wrt golang/freetype/truetype #45176

Open
sbinet opened this issue Mar 23, 2021 · 4 comments
Open
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@sbinet
Copy link
Member

sbinet commented Mar 23, 2021

hi,

it seems parsing the same TTF files (Liberation fonts, for example) leads to different font metrics values.
I'd like to understand whether that's coming from a bug/oversight from x/image/font/sfnt or from freetype/truetype.

What version of Go are you using (go version)?

$ go version
go version devel +732ea4c2dc Thu Mar 18 08:25:24 2021 +0000 linux/amd64

Does this issue reproduce with the latest release?

yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/binet/.cache/go-build"
GOENV="/home/binet/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/binet/dev/go/gocode/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/binet/dev/go/gocode"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/binet/sdk/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/binet/sdk/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="devel +732ea4c2dc Thu Mar 18 08:25:24 2021 +0000"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1145417185=/tmp/go-build -gno-record-gcc-switches"

What did you do?

https://play.golang.org/p/BM_FTuuxrxF

What did you expect to see?

reasonnable agreement between font metrics extracted from a TTF file via x/image and via freetype/truetype.

  • freetype/truetype doesn't extract XHeight, CapHeight nor CaretSlope
  • Descent is defined negative in one case, positive in the other
    but apart from those known differences, (and the glyph bounds), nothing really matches.

What did you see instead?

$> go run main.go
fnter: truetype: units-per-em=2048
fnter: opentype: units-per-em=2048
fnter: truetype: ext=main.Extents{Ascent:9.7998046875, Descent:-3.0322265625, Height:12.83203125}
fnter: opentype: ext=main.Extents{Ascent:9.052734375, Descent:2.119140625, Height:11.4990234375}
fnter: truetype: bnds=fixed.Rectangle26_6{Min:fixed.Point26_6{X:-1114, Y:-621}, Max:fixed.Point26_6{X:2666, Y:2007}}
fnter: opentype: bnds=fixed.Rectangle26_6{Min:fixed.Point26_6{X:-1114, Y:-2007}, Max:fixed.Point26_6{X:2666, Y:621}}
fnter: truetype: met=font.Metrics{Height:640, Ascent:580, Descent:136, XHeight:0, CapHeight:0, CaretSlope:image.Point{X:0, Y:0}}
fnter: opentype: met=font.Metrics{Height:736, Ascent:579, Descent:136, XHeight:338, CapHeight:440, CaretSlope:image.Point{X:0, Y:1}}
fnter: delta(t-o):   font.Metrics{Height:-96, Ascent:1, Descent:0, XHeight:-338, CapHeight:-440, CaretSlope:image.Point{X:0, Y:-1}}
fnter: opentype: met=font.Metrics{Height:2355, Ascent:1854, Descent:434, XHeight:1082, CapHeight:1409, CaretSlope:image.Point{X:0, Y:1}}
fnter: truetype: line-gap=196
fnter: opentype: line-gap=21
@gopherbot gopherbot added this to the Unreleased milestone Mar 23, 2021
@cagedmantis cagedmantis added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 25, 2021
@cagedmantis
Copy link
Contributor

/cc @nigeltao

@sbinet
Copy link
Member Author

sbinet commented Jun 25, 2021

gentle ping?

@nigeltao
Copy link
Contributor

Sorry, I don't have a lot of free time right now to investigate.

Do you know what the C FreeType library reports? (Ideally with a small reproducible C program).

@tinne26
Copy link

tinne26 commented Feb 11, 2022

I looked a bit into this, and I can share the following:

  • Extents: the comparison is not valid. The code used to compute them for freetype is taking the font bounds, which are the union of the font glyph bounds, while the opentype version uses the font metrics, which are derived from the font headers.
  • Bounds: these are actually matching, but since descents are negative in freetype, the Y axis is shifted between freetype and opentype, but the height is the same. Opentype here is matching font.Face.GlyphBounds() behavior (The glyph's ascent and descent are equal to -bounds.Min.Y and +bounds.Max.Y), which seems like the right thing for opentype to do, and it makes freetype behavior slightly strange, as it's also aware of the font interfaces but does not operate consistently with them.
  • Metrics: the Height value in freetype is simply wrong, as far as I can tell. It uses the scale right away (see truetype/face.go#L258), while opentype derives Height from the proper HHead Ascent/Descent/LineGap values. This means that line gap calculations in freetype are also meaningless.

Despite all this, I still have some concerns:

  • Extents: (edit: nevermind, seems like bounds height seems to exceed metrics height in almost all fonts I tried, or at most match it) freetype is reporting the bounding box that encloses all glyphs from the font, but that's bigger than the box reported by opentype, which is based on the sizes that the font defines in its headers. This means that some glyph (maybe some with carets or stuff above and below) exceeds the advertised font height itself, which seems ugly. Either that or the cbox is different from the bbox, but the difference seems too extreme for that to be the case. Someone who knows more about typography should let us know if any of these are common or there might still be an underlying issue here.
  • Metrics: the ascent is still slightly different. I'd assume some conversion or floating point issue, but I couldn't really pinpoint it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants