Inaccurate results from Font:getWidth()

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
grump
Party member
Posts: 631
Joined: Sat Jul 22, 2017 7:43 pm

Inaccurate results from Font:getWidth()

Post by grump » Fri Nov 03, 2017 5:59 pm

I'm getting unexpected values from Font:getWidth() with some fonts. The reported width for a given string is too small. This code should draw an "A" and a red box that encloses it:

Code: Select all

local gfx = love.graphics
gfx.setNewFont("Power.ttf", 128)

function love.draw()
	local test = "A"
	gfx.setColor(255, 255, 255)
	gfx.print(test, 10, 10)
	gfx.setColor(255, 0, 0)
	gfx.rectangle("line", 10.5, 10.5, gfx.getFont():getWidth(test), gfx.getFont():getHeight())
end
The result looks like this:
Image

It happens only with some fonts, and not with all glyphs of a font. And it doesn't seem to be a problem with the glyph width, since the error remains constant even with longer strings:
Image

Anyone know what causes this and how to fix it or work around it?

Fuzzlix
Citizen
Posts: 60
Joined: Thu Oct 13, 2016 5:36 pm

Re: Inaccurate results from Font:getWidth()

Post by Fuzzlix » Fri Nov 03, 2017 8:12 pm

May be the font has bugs. This happens sometimes when you are using free fonts.
If you want to keep using this font, i suggest you add a additional character to the string for (allmost) propper calculation. ( in your case:
gfx.getFont():getWidth(test.." ")
or
gfx.getFont():getWidth(test.."_")

grump
Party member
Posts: 631
Joined: Sat Jul 22, 2017 7:43 pm

Re: Inaccurate results from Font:getWidth()

Post by grump » Fri Nov 03, 2017 8:37 pm

Fuzzlix wrote:
Fri Nov 03, 2017 8:12 pm
May be the font has bugs. This happens sometimes when you are using free fonts.
If you want to keep using this font, i suggest you add a additional character to the string for (allmost) propper calculation. ( in your case:
gfx.getFont():getWidth(test.." ")
or
gfx.getFont():getWidth(test.."_")
The thing is, I'm writing a tool that processes user supplied fonts and it requires accurate glyph sizes from any font. This is one of the last bugs I need to fix before I can release an alpha version. It's a pretty useless tool if I can't fix this :(

User avatar
zorg
Party member
Posts: 2983
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Inaccurate results from Font:getWidth()

Post by zorg » Fri Nov 03, 2017 8:39 pm

Almost proper or not, the font itself looks italicized, meaning the top is leaning out of the "normal" glyph width area, in other words, the base of the characters are indeed inside that bounding box!

The error is constant exactly because the font itself defined some glyph sizes "incorrectly", or at the very least, not to what you expected; just because they reach further doesn't mean that the next glyph will start later, getting a bigger offset and messing things up (that's technically what you want to achieve).

The additional space included in the calculation wouldn't work as good, since apparently not all glyphs have this issue, so there might be some texts that wouldn't have glyphs being cut off, potentially messing with alignment.

Another hackish but workable way would be to just define (in pixels, let's say) how many extra units you want the total width to be appended by; that only needs to take into account the very last character in a line, for the above stated reason(s).

That said, it's most probably not an issue with Font:getWidth itself, it's a font issue... the only Löve related issue could be that the edges of such glyphs get cut off when drawing them, but i think that was already fixed.
grump wrote:
Fri Nov 03, 2017 8:37 pm
The thing is, I'm writing a tool that processes user supplied fonts and it requires accurate glyph sizes from any font. This is one of the last bugs I need to fix before I can release an alpha version. It's a pretty useless tool if I can't fix this :(
Processes as in? Turns them into an atlas/imagedata with all characters laid out nicely? Yes, that could cause some issues; usually you'd want to handle this with extra parameters that the users can supply for the fonts, like how much a glyph extends beyond its defined width.
Spacing's not an exact science.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.

User avatar
slime
Solid Snayke
Posts: 2897
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Inaccurate results from Font:getWidth()

Post by slime » Fri Nov 03, 2017 8:59 pm

It looks like the font file baked kerning information into its glyph advance values or something? In any case, text width, height, and other metrics are not the same as glyph texture dimensions inside the font's texture atlas. You can use Rasterizer and GlyphData objects to query that information if you really need, although they aren't well-documented right now.

grump
Party member
Posts: 631
Joined: Sat Jul 22, 2017 7:43 pm

Re: Inaccurate results from Font:getWidth()

Post by grump » Fri Nov 03, 2017 10:48 pm

zorg wrote:
Fri Nov 03, 2017 8:39 pm
Almost proper or not, the font itself looks italicized, meaning the top is leaning out of the "normal" glyph width area, in other words, the base of the characters are indeed inside that bounding box!
Yes, I considered that as well, but it does not explain why it only is that way for some glyphs and not for others:
Image
Processes as in?
As in taking every glyph from the font, turn them into an image, apply stuff to each glyph and save the whole thing as an ImageFont. RIght now some characters get cut off because I don't get the right widths. And I just discovered that the height is off as well in rare cases :(
slime wrote:
Fri Nov 03, 2017 8:59 pm
You can use Rasterizer and GlyphData objects to query that information if you really need, although they aren't well-documented right now.
I'll look into that, thanks!

Edit: Okay, I looked into it. GlyphData has two potentially relevant methods: getAdvance() and getBoundingBox(). I don't understand what they return; the values returned have no relation to the font size, and they are rather small. Can you give me a hint how to determine the exact bounds of a glyph in pixels?

grump
Party member
Posts: 631
Joined: Sat Jul 22, 2017 7:43 pm

Re: Inaccurate results from Font:getWidth()

Post by grump » Sun Nov 05, 2017 11:15 am

Bump. I still need some help here. How can I calculate/query accurate glyph bounds?
Image
This is unacceptable for my use case. I need to draw text on exact positions, I don't want it randomly shifted around in either direction.

I'm about to hack an awkward solution that draws each glyph on a large Canvas, gets the ImageData and uses mapPixel to calculate the pixel boundaries. I really don't wanna do that.

User avatar
Azhukar
Party member
Posts: 478
Joined: Fri Oct 26, 2012 11:54 am

Re: Inaccurate results from Font:getWidth()

Post by Azhukar » Sun Nov 05, 2017 12:19 pm

Post the font I'll give it a shot.

grump
Party member
Posts: 631
Joined: Sat Jul 22, 2017 7:43 pm

Re: Inaccurate results from Font:getWidth()

Post by grump » Sun Nov 05, 2017 12:34 pm

Azhukar wrote:
Sun Nov 05, 2017 12:19 pm
Post the font I'll give it a shot.
https://www.dafont.com/power-2.font

I'm looking for a solution that works with any font though, not just a hack for a specific one.

User avatar
Azhukar
Party member
Posts: 478
Joined: Fri Oct 26, 2012 11:54 am

Re: Inaccurate results from Font:getWidth()

Post by Azhukar » Sun Nov 05, 2017 2:06 pm

Played around with the undocumented rasterizer, managed to draw the actual outline of a letter.

Code: Select all

local testFont = love.graphics.newFont("Power.ttf", 128)

local rasterizer = love.font.newRasterizer("Power.ttf",128)
local glyphData = rasterizer:getGlyphData("A")
for k,v in pairs(getmetatable(glyphData)) do
	print(k,v)
end
print(glyphData:getBoundingBox())

function love.draw()
	love.graphics.setFont(testFont)
	local text = "A"
	local x,y = 10,100
	local fontHeight = love.graphics.getFont():getHeight()
	local fontWidth = love.graphics.getFont():getWidth(text)
	
	love.graphics.setColor(255,255,255,255)
	love.graphics.print(text,x,y)
	
	love.graphics.setColor(255,0,0,255)
	local gx,gy,width,height = rasterizer:getGlyphData(text):getBoundingBox()
	love.graphics.rectangle("line",x+gx+0.5,y+gy+0.5,width,fontHeight)
end
The metatable stuff is to get the undocumented function names.

Post Reply

Who is online

Users browsing this forum: No registered users and 17 guests