Skip to content

Adding fixed width font support to font groups #139

Closed

Description

@dparson55

Hi, I read the contributing guidelines and wondered if you would be open to a PR adding fixed-width font group support to the fontgenerator tool? I'm proposing a new argument that forces a specific width by centering the character (padding or cropping as needed). Example: -gfw 32 0 5 for a space character that's 5 pixels wide or updating the existing font group argument to accept an optional, fixed width. Example: -g 32 0 5.

I want to solve a spacing issue with the string "Cookies 325 12 mins". If I use the old font format to produce a fixed-width font, characters are expanded significantly further than necessary. I'm using canvas.getFont().setSpacing(0); for this example:

Image

If I use the new font format to produce a variable-width font whose characters are not expanded in size, the "Cookies 325 12 mins" string is hard to read due to the space character being only 1 pixel wide. I'm using canvas.getFont().setSpacing(1); here:

Image

So I made a change to the font generator to allow specifying a minimum character width and produced an easier to read, no wasted space result. I'm using the samecanvas.getFont().setSpacing(1); as above for this example:

Image Image

After thinking about the minimum width argument I added, I thought it would be more useful to allow specifying an exact width for each font group since it would provide a lot more flexibility. Narrow characters like spaces, colons, and periods could be given more padding compared to other characters and also someone could use the feature to create a fixed-width font while using the font group support of the new font format. So ultimately something like this would be possible:

set "PYTHON=C:\Apps\WPy64-3.15.0.1\python\python.exe"
set "FONT_SCRIPT=C:\Google Drive\Projects\Common\Arduino\libraries\lcdgfx\tools\fontgenerator.py"
set "OUT_DIR=C:\Google Drive\Projects\NRFMan\Code\NRFMan\fonts"

rem Character groups with fixed width. The space and colon would be expanded (while remaining centered)
rem while the underscore would be trimmed (both left and right sides would be trimmed, keeping it centered).
set "SPACE=-gfw 32 0 5"
set "COLON=-gfw 58 0 3"
set "UNDERSCORE=-gfw 95 0 5"

rem Character groups with the default widths.
set "DIGITS=-g 48 9"
set "UPPERCASE_LETTERS=-g 65 25"
set "LOWERCASE_LETTERS=-g 97 25"

set "FONT_GROUPS=%SPACE% %COLON% %UNDERSCORE% %DIGITS% %UPPERCASE_LETTERS% %LOWERCASE_LETTERS%"

set FONT=consolas
set SIZE=10
"%PYTHON%" "%FONT_SCRIPT%" --ttf "%OUT_DIR%\%FONT%.ttf" -s %SIZE% %FONT_GROUPS% -f new > "%OUT_DIR%\%FONT%_%SIZE%.h"

So that's the idea and I wondered if I should work on a PR. Or maybe I've totally missed something and you have an easier solution.

BTW you've probably heard this a zillion times but your driver is incredible: double-buffering support yet uses hundreds of bytes less program storage space compared to two other libraries I tried, absolutely amazing!

Activity

  1. lexus2k commented on Aug 2, 2026

    @lexus2k
    Owner

    Thanks for the detailed write-up (and the kind words!) — the before/after screenshots made the problem very clear.

    I went ahead and implemented this. Rather than a minimum width (-mw) or a separate -gfw flag, I took your second idea: an optional 3rd argument on the existing -g, since it's the most flexible and adds no new flag:

    -g <first> <count> <width>
    

    It forces every char in that group to an exact pixel width, keeping the glyph centered — narrow chars are padded with blank columns on both sides, wider chars are cropped equally. The width lands in the new-format jump table (which already stores per-char width), so it's free at runtime and works together with setSpacing().

    Your Cookies 325 12 mins case becomes, for example:

    ./fontgenerator.py --ttf consolas.ttf -s 10 \
        -g 32 0 5 \      # space -> 5 px
        -g 58 0 3 \      # colon -> 3 px
        -g 48 9 -g 65 25 -g 97 25 \   # digits + upper + lower, natural width
        -f new > consolas_10.cpp

    A couple of notes:

    • The optional width is only consumed when the next token is a bare integer, so a following flag (-fw, another -g, …) is never mistaken for a width.
    • Cropping to a width narrower than the glyph's ink prints a warning on stderr.
    • Don't combine a per-group width with -fw, which forces one global width onto every char.

    It's up as PR #141 and will land in the next release. I also converted the old tools/font_format.txt into a friendlier docs/font_format.md that documents this.

    If you get a chance to try it against your real strings, I'd love to hear whether the spacing comes out how you want — no need for a separate PR, but feedback very welcome.

  2. added a commit that references this issue on Aug 2, 2026
  3. dparson55 commented on Aug 3, 2026

    @dparson55
    Author

    Absolutely awesome, thanks for implementing this! I gave the new features a shot and the resulting text spacing for my project is perfect. The warning about cropping (and not failing) is also perfect for my use case as well, I'm purposefully wanting to make spaces and underscores the same width and want to crop the underscore rather than expand the space.

    Image

    The new start and end ascii char support is really nice and helped clean up some of the variables in the font generation script of my project, I could even select single characters like the COLON and UNDERSCORE which was great. PERCENT didn't work and I'm not sure about SPACE but this is no big deal at all, these are just weird groups I have to help optimize program space.

    @echo off
    
    set "PYTHON=C:\Apps\WPy64-3.15.0.1\python\python.exe"
    set "FONT_SCRIPT=C:\Google Drive\Projects\Common\Arduino\libraries\lcdgfx\tools\fontgenerator.py"
    set "OUT_DIR=C:\Google Drive\Projects\NRFMan\Code\NRFMan\fonts"
    
    rem The font generator script accepts groups of characters to include
    rem and we make use of that to significantly reduce the size of fonts.
    rem Ascii character codes https://www.ascii-code.com
    
    rem Format -g [start ascii code, or char] [ascii code length-1, or last char] [optional width in pixels]
    set "SPACE=-g 32 0 4"
    set "PERCENT=-g 37 0"
    set "COLON=-g : : 4"
    set "UNDERSCORE=-g _ _ 4"
    set "HYPEN_AND_PERIOD=-g - ."
    set "DIGITS=-g 0 9"
    set "LOWERCASE_LETTERS=-g a z"
    set "UPPERCASE_LETTERS=-g A Z"
    set "ALL=%SPACE% %PERCENT% %COLON% %UNDERSCORE% %HYPEN_AND_PERIOD% %DIGITS% %LOWERCASE_LETTERS% %UPPERCASE_LETTERS%"
    
    rem Variable-width font used for titles (only uppercase letters).
    set FONT=akashi
    set SIZE=11
    "%PYTHON%" "%FONT_SCRIPT%" --ttf "%OUT_DIR%\%FONT%.ttf" -s %SIZE% %UPPERCASE_LETTERS% -f new > "%OUT_DIR%\%FONT%_%SIZE%.h"
    
    rem Variable-width font for presets and settings (all character groups).
    set FONT=consolas
    set SIZE=10
    "%PYTHON%" "%FONT_SCRIPT%" --ttf "%OUT_DIR%\%FONT%.ttf" -s %SIZE% %ALL% -f new > "%OUT_DIR%\%FONT%_%SIZE%.h"
    
    rem Fixed-width font used for 00:00:00 timer display.
    rem Created with GLCDFontCreator.exe and only contains digits and colon (ascii 48-58).
    rem GLCDFontCreator.exe project file = share_tech_mono_24.lcd
    set FONT=share_tech_mono
    set SIZE=24
    "%PYTHON%" "%FONT_SCRIPT%" --glcd "%OUT_DIR%\%FONT%_%SIZE%.c" -f old > "%OUT_DIR%\%FONT%_%SIZE%.h"
    
    rem Fixed-width font used for 00:00 timer display.
    rem Created with GLCDFontCreator.exe and only contains digits and colon (ascii 48-58).
    rem GLCDFontCreator.exe project file = share_tech_mono_38.lcd
    set FONT=share_tech_mono
    set SIZE=38
    "%PYTHON%" "%FONT_SCRIPT%" --glcd "%OUT_DIR%\%FONT%_%SIZE%.c" -f old > "%OUT_DIR%\%FONT%_%SIZE%.h"

    The only little thing is when the name of the font gets changed due to the changed size. As I'm experimenting with different widths for the colon, I have to go check the generated font name and do a replace-all within the code. Not a big deal but it makes testing different widths slower. I imagine changing the generated name to be based on the -s argument would be a problem for other users.

    // set "COLON=-g : : 5" results in this font name
    const uint8_t free_consolas11x12[] PROGMEM;
    
    // set "COLON=-g : : 7" results in this font name
    const uint8_t free_consolas12x12[] PROGMEM;
    
    // set "COLON=-g : : 8" results in this font name
    const uint8_t free_consolas13x12[] PROGMEM;

    So many thanks for this incredible library and all the font features you've added!

    Image
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

questionFurther information is requested

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions