BangleApps
BangleApps copied to clipboard
Better Bangle.js fonts
There have been discussions about this in the past (I'm afraid I can't find any good links at the moment - they're probably on the forum). But vaguely related issues are https://github.com/espruino/BangleApps/issues/1145 and https://github.com/espruino/BangleApps/issues/1311
Problem
- Smaller fonts are too thin to be readable
- Big/small font styles don't match
- We don't support any characters outside ISO8859-1 0..255 codepage with current fonts
- We don't have a nice way of choosing what font to use right now - every app implements its own solution, using hard-coded fonts
Where we are
- There is an awesome free font in different sizes, specifically designed for the Bangle's display (well, pebble's): https://github.com/pebble-dev/renaissance/tree/master - it's in PBF format
- Bangle.js now supports PBF loading and rendering
- Bangle.js 2 also now supports Unicode so can render non-ASCII chars too
- There's also GNU unifont which I've turned into a PBF file so we can even support full chinese/japanese/etc locally
So what's the issue?
It'd be nice if we had a global way of changing fonts (eg by installing an app/library via the App Loader) - to either change the look and feel, size of fonts, or to add native support for more character sets.
While we could have a library/function that handled simple font selection, eg require("font").getFont("big"/"medium"/"small")
I don't think that's really useful enough.
I think we need something that'll choose a font based on some text and how much space there is. For example:
... = require("font").getFont({
w : 176, h : 120,
wrap : true,
text : "Hello this is a lot of text I want to display on the screen"
});
... = require("font").getFont({
w : 60, h : 60,
wrap : false,
text : "10m"
})
Any thoughts on this? I think the main things it needs to be able to handle are:
- Blocks of text in messages
- Small bits of text for labels or values (eg the 'run' app)
- Ideally being aware of images in the text - no point using a tiny font when there are images that are 16px high anyway
- Maybe returning the pre-wrapped, pre-cropped (with
...
) text as well as the font just to save on computing things twice - Specifying a maximum/minimum font height?
- Do we need to think about multiple text items? For example if we have a list (like in the Launcher) maybe we want to make everything in the list the same size?
I guess it might be good to think about existing apps that would benefit from this (messagegui
, launcher
, run
, E.showMenu
, E.showPrompt
, etc) and what kind of features they would need?
Also, I guess this may need to be something that runs quite fast - so do we actually want a built-in function that just looks at a list of available fonts? Or maybe we get it working in JS first, then if it's slow we look at moving it inside the firmware.
Examples
Yes, I like the sound of this, currently I have widclk
and widalarmeta
both displaying slightly different 7-segment fonts in the widget bar and having this more globally configurable would be nice.
Plan/points
I think getFont({ w, h, ... })
sounds like good, and later down the line, the layout library would be able to lean on it too.
- +1 for also returning the pre-wrapped/cropped text
- For whether we have min/max font height, I suppose it depends how we implement
getFont()
, although thinking about it from what I've typed below, I think just a height parameter would work, as we'd probably handle it as a max height:- If we have min/max, and there's two fonts that satisfy, do we always go for larger? (probably yes)
- If we just have a height passed instead, do we always go for the largest font under (or at) that height?
- So it's the same behaviour really, I don't think minimum size comes into play (except for this next point)
- For multiple text items, we could either have something in the API where we accept an array of max heights (of which we take the smallest), or we leave that logic up to the app itself - I'd vote for the latter, at least for the first implementation
I'd also like to add, for existing apps that would use this API / an initial implementation, I think it would be nice if we could let the user configure a single font for widgets, as mentioned at the start of my post
Initial approach
User config
- Ability to install fonts from the apploader, add entry to settings to enable/disable?
- Later implementation: permit configuring this via
settings
- Later implementation: permit configuring this via
- Permit user to upload/specify a font for the widget bar
Test apps
Perhaps we use run
and E.showMenu
as trials - run
for a full screen app with variable text, E.showMenu
as an "app" where we want consistent display of text for all items?
And yes, +1 for initial implementation in JS, then performance checks
Thanks!
Ability to install fonts from the apploader, add entry to settings to enable/disable?
I'm not sure I understand about enable/disable? Surely you just disable by uninstalling the font app?
Permit user to upload/specify a font for the widget bar
Maybe getFont
could have an optional use:"widget"
field, so that you could manually specify fonts for specific types of things? I guess having use:"numeric"
might be handy too so some fonts can choose to use 7 segment/etc.
While we can definitely add some settings to the font library (eg minimum font size, lock widget font, etc) I'm expecting that as long as we give enough info to getFont
, other users might contribute different font apps with different fonts/behaviour.
I'm thinking at first maybe we have one lightweight font app that mirrors the existing behaviour (with built in fonts) which is the default that gets auto-installed, and then a second app using the Pebble fonts mentioned above. And when we're happy with the Pebble font one I think we just swap over.
I'm not sure I understand about enable/disable? Surely you just disable by uninstalling the font app?
Good point - yes the enable/disable is unnecessary
Maybe
getFont
could have an optionaluse:"widget"
field, so that you could manually specify fonts for specific types of things? I guess havinguse:"numeric"
might be handy too so some fonts can choose to use 7 segment/etc.
Excellent idea, yes I think that'll work nicely for the font settings app being able to present the user with these use-types.
While we can definitely add some settings to the font library (eg minimum font size, lock widget font, etc) I'm expecting that as long as we give enough info to
getFont
, other users might contribute different font apps with different fonts/behaviour.
I'm interested in what font apps will do, are they effectively the setup for the builtin code that will sit behind getFont()
? Or will they provide getFont()
as a module, a bit like textinput
or provides_modules
?
I'm interested in what font apps will do
Right now I'm thinking they'll provide a font
module that provides getFont
- exactly as you say with provides_modules
. That way everything is already there to auto-install and get rid of duplicates
The Renaissance font looks good. To what extent will a raster font be speedier to draw than a vector font, especially at high resolutions?
You'd have to do some testing but as a guess I'd say any font is faster as a raster unless it's over 40px high. Also for smaller fonts the final rendering is much better.
Basically any normal font that's not filling the screen is better as raster
Some work towards this now in https://github.com/espruino/BangleApps/commit/591c1f8cc5e538f935c82d6dd1c31a48e3e4d822
It appears to work! https://forum.espruino.com/conversations/394649/?offset=25#comment17323914
Just checked and you can even send messages from (the latest) Gadgetbridge too
The ț character (U+021B) still doesn't appear in notifications on iOS, will test later today.
I am using the extended fonts
So does g.clear(1).setFontIntl().drawString("\u021B")
not do anything for you? What about other chars outside the 0..255 range?
I see it in 'all fonts' at least, and in extended fonts we do up to char code 1103 so 0x21B should be in there
It appears to work! https://forum.espruino.com/conversations/394649/?offset=25#comment17323914
Just checked and you can even send messages from (the latest) Gadgetbridge too
Great stuff! I'm quite keen on having consistent fonts across a few widgets I have, I'm quite short on time over the next few weeks but looking forward to incorporating this
Great! I should add that while there are now these font libraries available, the getFont
function described in this issue isn't properly implemented (it just returns Intl
for everything) - I just figured it was better to get the fonts in and usable in the messages app (even if it was a slight hack), rather than not having the functionality at all while I waited to try and implement the function nicely.
So ideally, before we start using the font all over the place we should probably implement getFont
a bit better in these libraries and then try and use that, so we're not spreading slightly hacky code all over the place.
Also thanks for your time recently @bobrippling - I know you've been busy :)
Perhaps we use run and E.showMenu as trials
Did a simple test with the run app and just swapped the two existing fonts with the same size.
The dithered text is almost unreadable and the other font is too broad for dist and time.
The latter could be solved by specifying a max width. But I think an other option would be to further categories fonts like width-height, boldness and maybe if it is a fixed width font or not. Then we could request a font according to these characteristics and get the most matching one with the result looking better.
Edit: I checked the code. Height and width (if it is fixed) are already stored. Should also be no problem to calculate the maximum/average width from the widths array for non fixed fonts.
The bangle js 2 menu uses one main fixed font. With a second smaller font as fallback for the items if the text doesn't fit in one line. And then cut them if it still doesn't fit.
To mimic the current behaviour an approach with requesting a font in a specified height would work. With an additional option for the item to use a smaller size or more condensed font if needed and additional cutting if needed.
A more advanced solution would be a min font size like it is used in the message app. But this would require more changes like adjusting the surrounding rect hight to the font height. Then make it a global setting and use it for settings, menu, the launcher and every app that wants to respect it.
Next I integrated the best fitting pebble renaissance font sizes into the run app. On the left the original. On the right the pebble fonts:
I replaced 6x8:2 with renaissance_18_bold.pbf and 6x15:2 with renaissance_28.pbf.
It looks ok even though 6x8 is fixed width and renaissance_18 isn't.
But both pebble fonts look smaller. The reason ist that there is a lot of spacing above the glyphs. See for example "G". It has a spacing of 7 above: https://github.com/pebble-dev/renaissance/blob/66df6992283adf97ba4689004dde7c51b5d03225/files/renaissance_18_bold.pbff#L485
https://github.com/pebble-dev/renaissance/wiki/PBFF-Format-description/
I am not sure. But this makes the fonts much less useful for us or what do you think?
Also one question: What is the reason the pbf format was included in espruino instead of converting the pbf fonts to the espruino format?
Thanks for that testing... apart from alignment, the 18_bold font looks good.
In terms of how we ask for the fonts, it seems like something like:
... = require("font").getFont({
w : 60, h : 60,
wrap : true, minHeight: 10,
text : "10m"
})
could work? The font lib could always check what the current app is and provide tweaks if needed.
What is the reason the pbf format was included in espruino instead of converting the pbf fonts to the espruino format?
The Espruino format assumes that all characters are in one range (eg 32..255), but PBF allows us to have Unicode characters to support different languages, and we can have for instance Korean, but with ASCII too, and not have to have all the characters inbetween.
The height thing is annoying, but actually that could be changed by modifying the font files - we can just adjust the reported height and maybe crop down/reposition the few chars that go out of range.
I just saw your forum posts as well: https://forum.espruino.com/conversations/383349/#comment17446938
Continuing on here...
I had assumed that for 36px we might just pixel-double the 18px for now as Bangle.js can just do that internally - but there are algorithms for pixel doubling so perhaps one could be built into the font converter tool: https://github.com/espruino/EspruinoWebTools/blob/master/fontconverter.js
the 18_bold font looks good
Yes, I like the look of the run app with the pebble fonts.
require("font").getFont ... could work?
Yes, I think it will work. I would give it a try with the run app and the menu: Depending on the systemwide configured fonts always the best one is chosen by getFont(). Then I try it with the current default fonts and compare it to only the pebble fonts and see how it works and looks. But a bold option for getFont() is neccesary otherwise the labels of the run app won't work.
The height thing is annoying [...] could be changed by modifying the font files
Yes, it should be possible to automate this.
We might just pixel-double the 18px.
The pebble fonts look nice because they are adjusted for each size. I attached the glyph "q" as an example in 18px and 28p. We won't get the nice rounding of the 28px when doubling the 18px. But if we really want to change to the pebble fonts then we can ask the author. Maybe he even has it vectorized. Or I can do the adjusments myself (already tried it with a few glyphs).
This code globally maps many of the system fonts to my version of the pebble fonts:
https://gist.github.com/Chriz76/75de579af0a844d126df94b1b303657b
This enables testing it in the existing apps without modifying them.
The fonts are adjusted to have less top offset and I also added a 36px version with nice rounded edges for the ascii glyphs: https://github.com/Chriz76/renaissance/tree/master/fonts
This is how the regular mapping looks:
And this uses an increased size mapping for better readability:
The fonts would still need some tweaking but if this looks promising to you, then I could make the fonts and the global replacement an app so everyone can try it.
This is really cool - and it looks like a massive improvement! I like the hack of just changing setFont
for now!
I'm off this coming week so I wouldn't be able to look at any big changes, but if you wanted you make this available in the app loader for testing I think that'd be great - and then maybe we could move towards also providing a require("font").getFont
function and slowly translating apps over to using that.
Created the app and made it available on my fork. It is highly experimental so I don't know if I should create a PR for it or if testers want to try it on my fork?
https://chriz76.github.io/BangleApps/?c=&q=scale
https://github.com/Chriz76/BangleApps/tree/master/apps/scalefonts
Next I will take a look at getFont. Got some nice insperation from the conversation here and also took a look how other wear OS solve it.
@Chriz76
I installed it on my watch and it really gives a boost to the visual presentation!
Some notes:
- It felt like menus were a little slower to render, being choppier when I installed
scalefonts
(together withkineticscroll
). Maybe that would go away if it was to be included in the firmware? - I really liked the feature to fill the available space as to help with legibility!
- I'm not sure, but maybe for really small sizes the font was not as legible as some other fonts. I got that feeling on e.g.
quicklaunch
s extension screen. But it may also just be a matter of being used to the other font. - Overall I dig this!
Thank you @thyttan for testing and your feedback!
I installed it on my watch and it really gives a boost to the visual presentation!
That's great news.
- It felt like menus were a little slower to render, being choppier when I installed
scalefonts
(together withkineticscroll
). Maybe that would go away if it was to be included in the firmware?
I am not sure if it will go away when it is build in the firmware. setFont doesn't do much so it is maybe the rendering of the pbf font that slows thing down, but this is already implemented in c code.
- I'm not sure, but maybe for really small sizes the font was not as legible as some other fonts. I got that feeling on e.g.
quicklaunch
s extension screen. But it may also just be a matter of being used to the other font.
I am using "14" as smallest of the pebble fonts and then fallback to the defined font. The reason is, that the pebble fonts are much smaller in reality than their name suggests. The letter "A" in size 14 is only 9 pixels high resulting in size 11 with one pixel at the bottom and one at the top. That is a bit frustrating and we might have to create more larger sizes.
So even my self created largest font size 36 is in reality much smaller. Here are the real sizes (already including space at the top and bottom) to compare it to the existing fonts:
{name: "renaissance36", size: 26},
{name: "renaissance28", size: 22},
{name: "renaissance24", size: 20},
{name: "renaissance18", size: 14},
{name: "renaissance14", size: 11},
{name: "renaissance9", size: 9}
@thyttan
- I really liked the feature to fill the available space as to help with legibility!
I developed a method like discussed before to achive this and would also enable a global font size setting and make apps scale to it.
function getFont(string, maxSize, bold, maxWidth, maxHeight, minSize, shortenToFit, wrap) {...}
test("Very long text made fit to screen. It is really long! And therefore get's cut as soon as it reaches 8px. Lorem ipsum. De bello galicco.", 36, false, 80, 80, 8, true, true);
test("Long text wrapped to fit to rectangle", 36, false, 80, 80, 8, true, true);
test("Long text made to fit one line",36, false, 80, 20, 8, true, false);
test("Fit one line", 36, false, 80, 20, 8, true, false);
test("Fit", 36, false, 80, 28, 8, true, false);
https://gist.github.com/Chriz76/5e69de3e56c8399b59a6364e15713209