-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Text metrics #356
Comments
Okay, so the above changes work, but I think there's a bigger problem. |
I just noticed this (separately) as well. textHeight() returns undefined in all my tests. Seems that also textAscent() and textDescent() are missing from Typography/attributes.js. I'm not aware of a simple fix for this, so I'm wondering if we might use Font.js (https://github.com/Pomax/Font.js) to grab these values (its MIT-expat licensed). Thoughts? |
I've looked on SO for a good solution and found a few articles. Looks like the only way to get the true pixel height of a given string is to manually calculate it by drawing to a canvas and examining the pixels. Bummer! That's what Font.js seems to do to get the ascent and descent, which it adds to get the height. Some reading: http://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/ |
thanks for looking into this @scottgarner and @dhowe. font.js looks light enough and seems to do what we want. I'd propose trying to wrap this in. alternatively, we leave |
Thoughts on handling the wrapping? At a minimum, at startup and whenever We could also try to build our own version with a similar technique, perhaps only creating the test canvas when the height, ascent or descent are checked. |
One idea might be to detect font-face rules in the css -- if one or more is found, then it creates the offscreen canvas, does the measurements, and creates one or more font objects (we might also add the loading into the preload routines)... So we would only be creating the extra canvas when fonts are used -- or, since in the p5.js case, as we already have a canvas, we may be able to use it for the measurements rather than creating an additional one (though if the text is larger than the user-created canvas, the measurements may not be correct). |
Size would definitely be a concern using the default canvas. Font.js goes with 1000x1000. It has placeholder logic for slicing up longer segments, but it isn't implemented. I might also worry about state? If you were in the middle of a draw loop and added some text, you'd have some time where your canvas was taken over to be solid white with black text. Since there's lots of looping through pixels, it might even last a couple of frames? Looking for font-face is an interesting idea, but we'd want it to work with system fonts, too, and even generics like "serif" and "sans-serif". I experimented with some techniques from this SO post that use spans instead of a canvas, but the results aren't quite what I'd expect: |
I think we are only talking about (max) ascent + descent (which can be computed once in preload), no? If we scale these values to the font-size, we get a (max) height measurement for any text string. This will not be a tight bound (taking into account descenders, etc.), but this is also the case in the Processing API -- it is only textWidth() that takes a string argument, textAscent() and textDescent() are constant for a given font/size. So I'm not clear on the problem mentioned above in the draw() loop... And I'm not sure we can get measurement data for system fonts -- or at least not according to Font.js: 'system font files cannot be inspected, as they do not have an associated font.metrics object...' But the span option looks interesting -- what kind of results did you see @scottgarner ? |
Oh, right. I see what you mean. I guess my expectations for textHeight would be the actual pixel height for a given string. And actually, textHeight() doesn't seem to exist in Processing proper? I'm remembering now just adding the ascent and decent on a project years ago. In that case, the span trick might be the right solution for ascent and decent? It also looks like the HTML "living standard" has a much more robust treatment of text metrics, including bounding boxes, that we might consider down the line. http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting.html#textmetrics |
Yes, I think whatever we do is probably a stop-gap until better support in the browsers... I also just tested the span solution and it seems OK, at least for system fonts. Also, and perhaps this is (or should be) a separate issue, but is there a method for loading non-system fonts (font-face fonts, etc., and also making sure they are preloaded before drawing starts)? |
I think the idea is that PFont would handle that? It's mentioned in #60. |
yeah the whole pfont thing needs some rethinking, and I think we could feel free to change up the API with this to suit the web. at the minimum it'd be nice to have an easy way to hook in a font without having to touch css. with the text metrics. if some end up just being more hassle than they're worth we could probably cut things for now, considering support will likely change and improve in the future. it sounds like both of you have more thoughts and experience with text so I'm happy to go with whatever you conclude. |
I'll take a stab at ascent and descent next week based on the span method, One question of approach: Do you think it would be better to calculate the On Sat, Sep 6, 2014 at 6:43 PM, Lauren McCarthy notifications@github.com
|
maybe calculate once when textAscent or textDescent is called if font properties have changed since last calculation? |
Seems possible we calculate ascent and descent once (for each font) on load, then store these data as part of PFont, then scale based on font-size when textAscent/Descent are called... (this way we don't have to worry about creating DOM elements in the draw loop) |
Okay, I took a stab at this, but I'm not sure if the approach is ideal. Because it's such a weird, hacky solution, I only use it if textDescent() or textAscent() are explicitly called. Calling one stores the other property as well and the values are essentially cached until the context font properties are changed. Right now, that just clears them instead of recalculating because I don't want to go through the weirdness unless someone is definitely using these functions, though I'm probably being overly cautious. This could be done in a more refined way, like @dhowe's idea of storing a ratio that would work at different sizes for the same font, but I thought that might be better as part of the PFont implementation instead of reworking everything here. |
One other thing here. Because the font properties aren't applied at the beginning of text() like they were originally, the defaults aren't applied unless a property is explicitly changed. @lmccart, what would be the appropriate place to initialize font properties for the main canvas? Basically, |
great stuff! you could call _applyTextProperties just after createCanvas in _start or somewhere in _setup, whichever makes more sense |
Removed textHeight() and add textAscent() and textDescent() per #356.
Oh, a comment in _start() made me realize that the default canvas will very likely be replaced. Maybe it's best to do it in createCanvas so all canvases are covered? I suppose it would also need to be called in createGraphics? A little worried I'm missing something here. |
ah I see. maybe better to do it in the p5.Graphics constructor then? |
Hmm. That's the right place, I think. The problem is that we wouldn't want to apply tweaked settings to a p5.Graphics because they should aways start pristine (like with default stroke, fill, font etc.). So there should probably be a default set of font attributes that always get applied on canvas and graphics creation. The quick fix would be to just set That might be the best option for now? Once we get into PFont, we could have a default p5.Font created on start up that is always applied to new canvases/graphics. |
Setting font defaults for new canvases/graphics per #356
p5.Font now in place -- this should be closed |
textWidth()
seems to only return the correct value after callingtext()
.I think it's just a matter of setting
this.drawingContext.font
before callingthis.drawingContext.measureText()
, but I'm not sure what would be the preferred approach.I'm thinking the best plan is to take this out of
text()
, which is inefficient since there's no reason to set the styling for every call if it hasn't changed, and instead setthis.drawingContext.font
anytime a relavant property (size, style, font) is updated. This way everything is always up to date fortext()
,textWidth()
andtextHeight()
;The text was updated successfully, but these errors were encountered: