Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(9 edits) (+1)

This is a bug in PixelForge that I just tracked down today. Basically, there is this value in .ttf fonts called “Units Per Em” or UPEM. This acts like a scale factor; the larger it is, the smaller the font renders.

PixelForge seems to export .ttf files with the UPEM set to 133.33% larger than it should be.

This is also the reason why the suggested font sizes do not actually render text pixel-perfectly.

Because the source code is not available, I cannot fix this issue with PixelForge directly. I can, however, fix the issue after exporting by scaling the UPEM by 75%.

The easiest way I found to do that was with Python.

Save the following script as fix_pixelforge_ttf_scaling.py:

#!/usr/bin/env python

import sys
import argparse
from pathlib import Path

from fontTools.ttLib import TTFont


# Fixes the scaling on PixelForge TTF fonts
def fix_pixelforge_ttf_scaling(font_in_path, font_out_path, scale=0.75):
    font_in_path = Path(font_in_path)
    font_out_path = Path(font_out_path)
    
    font = TTFont(font_in_path)
    
    head = font['head']
    head.unitsPerEm = round(head.unitsPerEm * scale)
    
    font.save(font_out_path)


# Parse arguments
def parse_args(args):
    parser = argparse.ArgumentParser(
        description="Fixes the Units Per Em (UPEM) value of a TTF file exported from PixelForge to make the suggested font sizes accurate.\n"
                    "Basically, there is a bug in PixelForge that makes the UPEM 133.33% larger than it should be.\n"
                    "This simply scales the UPEM down by 75%.",
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    parser.add_argument("-i", "--input", dest="input_font", type=str, required=True,
                        help=f"the path to the TTF to convert"
                       )
    
    parser.add_argument("-o", "--output", dest="output_font", type=str, required=True,
                        help=f"the path and filename of the desired output TTF file"
                       )

    parsed_args = parser.parse_args(args)
    
    # Interpret string arguments
    input_font = Path(parsed_args.input_font)
    if not input_font.is_file():
        parser.error(f"the file \"{input_font}\" does not exist")
    parsed_args.input_font = input_font
    
    output_font = Path(parsed_args.output_font)
    if input_font.resolve(strict=False) == output_font.resolve(strict=False):
        parser.error("the output font file must be different than the input one")
    parsed_args.output_font = output_font
    
    return parsed_args


def main(args):
    print()
    parsed_args = parse_args(args)
    
    fix_pixelforge_ttf_scaling(parsed_args.input_font, parsed_args.output_font)
    print(f"Saved fixed TTF to \"{parsed_args.output_font}\"")


if __name__ == "__main__":
    main(sys.argv[1:])

You will need to install the fontTools package with pip before running this:

pip install fontTools

Run the script and fix your files as follows:

python fix_pixelforge_ttf_scaling.py -i "path/to/myfont_raw.ttf" -o "path/to/myfont_fixed.ttf"

Update!

Just a heads up, I eventually figured out that there is no bug in Pixelforge, it actually is outputting the font at the correct scale! The issue is actually that we as users didn’t understand how fonts work!

Points (pt) are NOT equal to pixels (px)!!! Points are generally in reference to a 96 DPI environment and pixels are generally in reference to a 72 DPI environment! So a pixel is equal to 96/72 points, which simplifies to px = 4/3 pt. That’s why the fonts look 3/4 as large as we expect… Because we have been taking the points size given to us via the export pop-up, and using it as the pixel size!

All my Python script is doing is converting the font to a non-standard scaling system that will render incorrectly next to other fonts of the same size. In actuality, the real fix is to use either points @ 96 DPI as your unit when setting the text size, or you convert that points value to pixels @ 72 DPI.

So for example, I made a 5 px tall font with 1 px of spacing between rows, making it 6 px tall total. The export dialog will tell me to use multiples of 6pt @ 96 DPI. If I want to figure out what pixel sizes to use instead of points, I multiply 6 by 96/72 (or 4/3), and that gives me 8px @ 72 DPI. So my font needs to be rendered at multiples of 8px @ 72 DPI.

If you still want to use the script, it is the only way to get Pixelforge to re-import a .ttf it’s exported in the past (I think the TTF import code also confuses points and pixels like we all did). But just be warned that if you try to use that modified font next to other fonts set to the same size, they will not look the same size. If you are using the pixel font as your sole font, you can probably get away with using the scaled version my script creates.

Pixelforge should really have this info included beneath the suggested points sizes, that way users won’t be confused, and they are implicitly educated that points are not equal to pixels, which it seems is not common knowledge.

thanks

so uh when i start it i get this error (im not familiar with phyton scripts at all) can you like tell me what to do here?

copy paste the code into a new file called fix_pixelforge_ttf_scaling.py

in the terminal use the "cd" command to enter the folder with both the python file and the font file

run the command with your font name

(+1)

you dropped this 👑

Oh my gosh you saved my sanity, thank you!

(7 edits)

Just a heads up, I eventually figured out that there is no bug in Pixelforge, it actually is outputting the font at the correct scale! The issue is actually that we as users didn’t understand how fonts work! Details have been added to the main comment, but basically, points (pt) are not equal to pixels (px), and you have to do a unit converaion to find your text size in pixels or it will display 3/4 as large as you would expect.

When using a pixel font maker, the expectation from the user is that setting the font size to "8" (no units are displayed in the Pixel-Forge settings, you have to read the warning on export carefully to notice the discrepancy) would mean it renders accurately when set to 8px in a given pixel-perfect software. If you're using a software with the pt scale instead, the font would be scaled anyway so there wouldn't be any loss of function. Since the creator here has set the font settings to use pt rather than px, however, fonts exported with this tool can't be used in pixel-perfect software settings, contrary to other pixel font softwares.

This is a mismatch between what these tools are commonly used for and the way this particular tool is designed. I'd suggest to the creator that switching the scale from pt to px would significantly improve usability. I was a bit disappointed as this seems a very well designed tool, otherwise, and is one of the only ones I've seen with support for such a large set of glyphs.

So the issue then is that they will not display properly when used in software next to other fonts. Any font that supports specifying sizes in pts instead of px will take care of the unit conversion behind the scenes. But if the creator changed the scale, it would display too large everywhere except where the users weren’t incorrectly specifying their desired size units.

IMO the two best options (either or maybe both could be done):

  • Add the px sizes in the export dialog and make them ABOVE the pts values
  • Add a “Use pixel-centric export scaling (may display incorrectly in some places)” toggle option to the font file which will export the TTF with the scale factor my Python script converts to

Hi! I opened a new thread to explain the current handling of TTF export sizes, and to collectively discuss what changes will be done to address these issues. It would be great to get feedback and ideas on the planned changes to fit all use cases!

Hi! I opened a new thread to explain the current handling of TTF export sizes, and to collectively discuss what changes will be done to address these issues. It would be great to get feedback and ideas on the planned changes to fit all use cases!