Custom Fonts Module [CFM]

Hello, today I wanted to tell everyone about a module made by me and @LogicUndefined, this module is used for generating a text with a custom font.

Ever wanted to use your own fonts? But never could because of Roblox’s limited fonts library, well that’s the problem we faced when working on our new and upcoming horror game. We needed a font that’s pixelated and we didn’t like the current one. So we decided why not just make our own font module [CFM]

Showcase

image

The font we will be making in this tutorial!

Necessary stuff

The app BMFont made by AngelCode [BMFont - AngelCode.com] this is used for getting the font into the format XML and PNG

The Custom Font Module:
https://create.roblox.com/store/asset/16177689562

We Released A Brand New Plugin That Automatically Converts The .Fnt To The Right Format!
https://create.roblox.com/store/asset/85538084957433/Font-Converter

It includes a pre-made font.

Not so necessary stuff

Demo place:
Custom Font Demo.rbxl (60.1 KB)

Files if wanted to tag along:

NovemberFOnt_0

NovemberFOnt.xml (11.3 KB)

Tutorial
  1. How does this even work?

We used the XML unpacker [GitHub - jonathanpoelen/lua-xmlparser: Fast, simple, pure Lua library for XML parsing] made by Jonathanpoelen on GitHub.

It generates an image label and it uses the ImageRectOffset and ImageRectSize property of it to change the image to the current letter.

  1. How do you even get a custom font in Roblox?

Well you can use different sites such as www.dafont.com, google fonts and more! You download the font and save it anywhere. Then once you open the BMFont app you will be prompted by something like this:

To change the current font you need to go into Options > Font Settings.

Here you can add the custom font by pressing “Add font file” and selecting the downloaded font. Keep in mind the format has to be a .ttf (windows font file)

(if it doesn’t appear in the “Font:” list then just reopen the app)

Then after you set the custom font it should look something like this:

Press ok and head on into Options > Export Options.

Once you’re in the Export Options tab set the Spacing “A” and Spacing “B” to 1
The rest of the settings I don’t know that they do so I just leave them how they are right now.

After you did this go to File Format at the bottom of the page and set “Font descriptor:” to XML.
Then set “Textures:” to PNG, then press Ok.

This is how Export Options should look like:

Now you should be able to see your font in the main window like this:

As you can see the letters have changed to the new font we picked!

When you’ve finished all these steps go ahead and go to Options > Save bitmap font as [Ctrl + S]
and pick a location for the font, remember the font path.

These are the 2 files that you will get after Saving the bitmap font:

image

You might say, where is the XML file tho? Well the .fnt file is secretly but not so secretly a XML file, So go ahead and open it up using whatever program you want. For the tutorial ill use Visual Studio Code

This might look intimidating, but don’t worry it is! How are we going to port this to Roblox you might say, Well we are going to use this as a template:

This file is also in the demo place and in the module so don’t worry about not having it!

return {
	[32] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[33] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[34] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[35] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[36] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[37] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[38] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[39] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[40] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[41] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[42] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[43] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[44] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[45] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[46] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[47] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[48] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[49] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[50] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[51] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[52] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[53] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[54] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[55] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[56] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[57] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[58] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[59] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[60] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[61] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[62] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[63] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[64] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[65] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[66] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[67] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[68] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[69] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[70] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[71] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[72] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[73] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[74] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[75] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[76] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[77] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[78] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[79] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[80] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[81] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[82] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[83] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[84] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[85] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[86] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[87] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[88] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[89] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[90] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[91] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[92] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[93] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[94] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[95] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[96] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[97] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[98] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[99] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[100] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[101] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[102] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[103] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[104] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[105] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[106] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[107] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[108] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[109] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[110] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[111] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[112] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[113] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[114] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[115] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[116] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[117] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[118] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[119] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[120] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[121] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[122] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[123] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[124] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[125] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
	[126] = {x = 0, y = 0, width = 0, height = 0, xoffset = 0, yoffset = 0, xadvance = 0, page = 0, chnl = 15},
}

They are not in order so 32 is not A, so make sure to do all of them before playtesting!

Yeah, we’re sorry but this the only way we found!

WRONG!!! We Just Released A New Plugin! https://create.roblox.com/store/asset/85538084957433/Font-Converter

image
The module that we need the big text for is FontData, so just replace that one with the one you just made!

You might also noticed there is a ImageID String in the font folder as well so upload the font image

[Example: ]
image

And get the imageID of it and change it inside the ImageID string value.

After this inside the “ExampleUsage” script (if you are looking at the demo place) inside the

Text.new("November", true) 

change “November” to what your font is (has to be inside ReplicatedStorage > Text > Fonts)

A demo script showcasing a basic text generation:

-- @LogicUndefined
-- @N_ckD

repeat task.wait() until game:IsLoaded()

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

-- Require text module
-- For more configuration, read the text module.
local Text = require(ReplicatedStorage.Text)

-- Create the text gui that the text must be parented to.
local TextGUI = Text.GetTextGUI(1)

-- Parent the text gui
TextGUI.Parent = Players.LocalPlayer.PlayerGui

-- Let's create a testing text.
local text = Text.new("November", true)
--[[
	@param [font_name: string] The name of the font to use. Found in the "Fonts" folder under the text module.
	@param [automatic_update: bool] Whether or not to automatically update the text every render step.
	If you choose not to update the text automatically, you will need to manually call text:Update(delta_time) whenever necessary.
	
	@returns [text: Text]
]]

-- Parent the text to the text gui
text.Parent = TextGUI.TextParent

-- Position the text in the center.
text.Position = Text.GetVirtualResolution() / 2

-- Center the text.
text.TextCentered = true

-- Set the text size. This is a factor of scale.
text.TextSize = 2.3

-- Display some text.
text.Text = "Hello World!"

Then just play the game and look at the brand new font you just made! Congratulations !!

This is my first ever #resources:community-resources Post so please let me know what I can improve on and and tips are appreciated! If you need any help please don’t hesitate!

39 Likes

Lol


Your first resource post is actually pretty nice. Good explanation, pictures, humor. And it’s actually something I may use
Well done

2 Likes

Interesting, I’m trying to replicate the look of a ROBCO terminal from fallout and think this could help.

1 Like

Please let me know if you are going to use this, and send me some pictures. I really wanna see it!

2 Likes


I’m basically redoing a really old game of mine which happens to be a Undertale like game so I thought it would be cool to have the wingdings in there and it works! Just forgot to do the transparency background but its pretty much useless as it would be mostly used with black backgrounds anyways, cool module honestly, good job!

3 Likes

Small Tip to other users: If the picture is just completely Black or white use these Export Settings.
For me the png was completely Black so i recommend just testing around.


Also i dont know if its a Module Issue or a Font Issue but the “!” in “Hello World!” is in the “d”, any way to fix it?
image

4 Likes

Also i dont know if its a Module Issue or a Font Issue but the “!” in “Hello World!” is in the “d”, any way to fix it?

It’s probably a combination of both.

You can probably just manually edit the offsets in the font data.
image
Look for unicode 33 for the “!” character, and alter the xadvance value to be larger.

1 Like

I tried using the cfm from egomoose but it was pretty old and broken… also the fonts looked super pixelated even if they werent supposed to. Hoping this will fix all my problems

1 Like

It shouldn’t as long as the image size is not too big for roblox to downscale it to a size where its pixelated.

That didn’t work, xoffset did do the job tho

3 Likes

Some fonts might be broken because of the actual image and not the module itself.

1 Like

Great module! I however, did not feel like manually going in and assigning the values in the Roblox format

So I decided to say “No” to this and I have written a bit of python code to translate the .fnt file to text that is aligned with the template provided. Granted this took longer than it probably would’ve to just manually enter the digits, but at this point I don’t care.

If you are interested in using it, just head to this link and press “Fork and Run” ofc you’ll need to upload your file using the three dots at the top right of the files category after forking it though.

The Code (If you prefer to run it locally yourself):

The Code
import os

def import_font_file():

  font_files = []
  for f in os.listdir():
    if f.endswith(".fnt"):
      font_files.append(f)
    
  if len(font_files) > 1:
    print("Multiple .fnt files found in the directory. Please only upload one .fnt file for conversion at a time.")
    exit()
  elif len(font_files) < 1:
    print("No .fnt files found in the directory. Please upload a .fnt file for conversion.")
    exit()
  elif len(font_files) == 1:
    file = font_files[0]
    return file
    
def convert_to_rblx(file):     
  with open(file, 'r') as file:

    values = [] 
    
    contents = file.readlines()

    for l in contents:
      l = l.replace(" ", "")
      if l.startswith("<charid="):
        
          temp = ''
          temp_list = []
          for c in l:
              if c.isdigit():
                  temp += c
              else:
                  if temp:
                      temp_list.append(temp)
                      temp = ''
          if temp:
              temp_list.append(int(temp))
          values.append(temp_list)

  values_rblx = []

  values_rblx.append("return {\n")
  
  for i in values:
    values_rblx.append("	[" + i[0] + "] = {x = " + i[1] + ", y = " + i[2] + ", width = " + i[3] + ", height = " + i[4] + ", xoffset = " + i[5] + ", yoffset = " + i[6] + ", xadvance = " + i[7] + ", page = " + i[8] + ", chnl =" + i[9] + "},\n")

  values_rblx.append("}")

  with open("Output", "w") as output_file:
    for v in values_rblx:
      output_file.write(v)

  print("Conversion complete. Output file saved in \"OutPut.txt\", copy and paste this data into Roblox.")
    
file = import_font_file()
convert_to_rblx(file)

I suppose you could make this in lua, that way you could translate it in the actual game, within a script, but I’m not very good with lua and I do not feel like making that when I’ve already committed to this lmao

Hope this can help somebody, took longer than I thought it would.
Thank you for the module btw!

5 Likes

Oh, thank you. This is useful I might make a plugin for this module in the future and use this code of yours if needed. Thank you!

2 Likes

After making the plugin I realized an error in your code. It doesn’t work if there’s negative values.

In this example:
{5435FBC4-59FE-40D9-B3BA-8B75BB9660B7}

it was converted using the code you provided but in the .fnt file the xoffset is -1

{FBA22F96-9AC9-4D0A-806C-581CAE5DCEBF}

I have fixed it , plugin will get some UI and be published.

Output:

A Brand new plugin for this has been published, that helps with the conversion of the .fnt file into the right format. Plugin link: https://create.roblox.com/store/asset/85538084957433/Font-Converter

1 Like

Knew I must’ve missed something, good catch.

I should’ve done a bit more testing with more variety in values. I’m glad to see this project is still getting attention.

1 Like

I got a fix for it, dw abt it I’ve published my plugin.

Not that hard of a fix

1 Like

Hello! i recently stumbled over this post and i must say, this is very cool. I have 2 questions:

  1. i followed the tutorial exactly as it said, did i miss something? because there are black background in the letters when i try to print the text.

  2. Is there a way to change the size of the text to scale at the parent? So it automatically updates like a textlabels TextScaled property

again, very cool module!

PS yes, i know scripting, you can tell me a way to do it, i just have a hard time understanding the variables and the math going into the sizing of the text.

1 Like

Yo wsp, the module is greate, i just wanted to kno when are you gonna update it at lest adding text fit so that it automaticaly aligns just like the label. That would be greate !

1 Like