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.