Custom Font Service

So over the past few days I’ve been working on a custom font API that will allow you to use your own custom fonts in your games and I think it’s ready for a public release.

The API is composed of one FontBin Backpack that contains all of the information about your various custom fonts, one FontCreator ModuleScript that creates an API for each individual font, and one FontDisplayService that puts those APIs to use to allow you to display your custom fonts. All of those objects go directly into the ReplicatedStorage. This post will serve as a features list, an API reference, and a tutorial on how to use my custom font API.

ROBLOX MODEL LINK HERE

  • You can enable wrapping to wrap your text inside of the target frame
  • You can enable word detection in combination with wrapping and then entire words will be wrapped together instead of splitting them up
  • You can set the font size to anything you want
  • There is support for multi-character symbols. More on that in the API reference.
  • Automatically generate a text bounds Vector2 when you create your text, or use the font’s specific object to get the bounds of any string of text

Font Module Structure:
This is the structure of table returned by the font module. The name of the font that you will use to reference it is decided by the name of the module.

	.source = Content String sourceImage
		-- This is the location of the image that all of your characters are loaded from
	.lowercase = String lowercaseMode
		-- This is how the font will handle case characters.  It can be one of three values:
		-- "all" - Every character is transformed to its lowercase form before writing it
		-- "determinate" - Only characters that don't have capital forms are transformed
		-- "none" - No characters are transformed
	.spaceWidth = Number pixels
		-- This is the is the size of the space character at the font's native size
	.letterSpacing = Number pixels
		-- This is the number of pixels between each character at the font's native size
	.lineSpacing = Number pixels
		-- This is the number of pixels between each line at the font's native size
	.map = Table characterMap
		-- This contains the dimensions of every character and symbol on the image.
		[String character] = {Number topLeftCornerX, Number topLeftCornerY, Number sizeX, Number sizeY}
	.extensions = Table extensionsMap
		-- Some characters reach below or above the line they belong on, and this map allows you to do that
		[String character] = {Number pixelsAbove, Number pixelsBelow}
	.specialWordCharactersList = Table specialWordCharacters
		-- Word detection is done based on characters that make up words.  If you want one of your characters to be interpreted as a letter then you need to include it here.  Characters that are automatically interpreted as letters are [i]abcdefghijklmnopqrstuvwxyz.?!1234567890/-\'";:,[]{}()<>[/i]

Font Object:
All the properties here are read-only.

	.source = Content String -- same as in the font module
	.lowercase = String -- same
	.spaceWidth = Number -- same
	.letterSpacing = Number -- same
	.lineSpacing = Number -- same
	.map = Table -- same
	.extensions = Table -- same
	.specialWordCharactersList = Table -- same
	.baseHeight = Number pixels -- The native resolution for the font

	.transformCharacter(String character) -- Returns String transformedCharacter.  Transforms the character into the usable version according to the lowercase setting.
	.getCharBounds(String character) -- Returns Table bounds.  Same thing as fontObject.map[fontObject.transformCharacter(character)].
	.getStringSize(String string, Number fontSize) -- Returns Vector2 size.

Font Creator:

	.load(String fontName) -- Returns FontObject.

Font Display Service:

	:Preload(String fontName) -- Loads the font object and the font source so it can be quickly retreived and displayed in the future.
	:WriteToFrame(String fontName, Number fontSize, String text, Bool wraps, GuiObject frame, Bool wordDetectionEnabled) -- Creates a bunch of imagelabels in the provided frame to represent the characters.  If wrap is true, the text will wrap inside of the frame.  If wordDetectionEnabled is true then entire words will wrap together.  Returns Vector2 maxBounds.

Before you make your own font, you should understand the limitations.

  • Generated text wrapping is not recalculated on run time. You need to destroy and then generate new text to wrap to the frame’s new size.
  • There is no text alignment API. You need to calculate that on your own.
  • Fonts can only be composed of a single source image. This will limit the number of characters in relation to the native font size.
  • Native font size is calculated based on the largest character. If you have characters that reach below or above the lines and you don’t want those extensions to be included in the calculation then you need to use the extension API (FontModule.extensions). More on how to do that in the tutorial.
  • Font sources are not automatically loaded. If you want to load the source without displaying it, use FontDisplayService:Preload. More information on that in the API section.

Now that you understand what you can and can’t do with this, you’re ready to set up the custom font service in your game. Start by grabbing the model in the link near the top of this post. Included in this model are the three parts of the custom font service: Backpack FontBin, ModuleScript FontCreator, and ModuleScript FontDisplayService. Place all of these in your ReplicatedStorage so that any script can require them and they can all require each other. Now your font service is set up (praise modules!) so you’re ready to create your own font. If you don’t want to invest time right yet, however, there are two fonts included in the model named Showcard (a cartoony font with outlines) and PressStart2P (a monospace 8-bit font), and you can skip ahead to step 4 and just use these fonts instead of your own font.

For the tutorial on how to create your own font I’m going to be using Paint.NET. I have no photoshop :frowning:
I’ll be showing you how I created the showcard font, which is included in the model.

Step 1 - Create a 1024x1024 image and fill it with characters
This image is your source image. It contains all of the characters in your font. Here’s the image I used for showcard:

Imgur made the background white but you want your background to be transparent or else your characters are going to have ugly boxes around them. You may have noticed that there are only capital letters here. That’s because showcard is a caps-only font, so every letter appears capital. My font API supports this, and I’ll show you how to enable that support later on. You’re okay to go ahead and upload the source to roblox.

Step 2 - Create your font’s ModuleScript
You can read all about the font module API back at the API section, and if you need help you can take a look at the modules that are included in the FontBin. Here’s Showcard’s module’s source:

Your font module needs to be in the FontBin or it will not be recognized. Since Showcard is, as I mentioned earlier, a caps-only font, I’ve set lowercase to “all”, which means that all of the input characters will be transformed into their lowercase forms when displaying this font. Now, instead of adding a capital and lowercase character for each letter, I only need to add a lowercase character and both forms of the letter will use that character’s dimensions. The map and extensions tables were collapsed in this image to better-convey the general idea and I’ll go more into those tables in the following steps. Right now you need to set your normal properties: source, lowercase, spaceWidth, letterSpacing, and lineSpacing.

You may also notice the lack of a specialWordsCharacterList table. That table is optional, since not every font will have special characters. Also, remember that the name of your ModuleScript is the name that you will be using to refer to the font when you access the API.

Step 3 - Create your character map and your extension table
Your font’s map is contained inside of the map table in the font module. It is structured like this:

The four numbers are, respectively, TopLeftXPosition, TopLeftYPosition, SizeX, and SizeY. Paint.NET’s status bar will show you these numbers in order if you put a rectangle selection around the character, as shown below:

This is easily the longest and most boring part of creating the font and I really wish I had a better way of doing it. Sorry :frowning:

While you’re creating your character map you may run into characters which reach above or below the text line. The extension table serves the purpose of conveying these anomalies to the other scripts to make sure the characters appear as intended. Here I’m going to use the as an example. This is the bounding box that I've defined for in the map table:

This box uses a generously large SizeY value, but even that isn’t enough to contain the entire character. My options are to either increase the size of the box (thereby decreasing the relative size of the other characters when the service calculates the native font size), or I can use the extensions table, which ensures that oddly-sized characters are displayed properly. Since I’m using the extensions table, I’m going to go ahead and set the boundaries to what I have now, even though they don’t contain the entire character.

Now I need to figure out what values to use in the extensions table. The extensions table is set up this way:

String character = {Number topExtension, Number bottomExtension}

So what I’m looking for is the top and bottom extensions. These are the number of pixels that the characters exceed the boundary on the top and bottom directions, respectively, so I can do something not dissimilar to what I’ve been doing to the rest of the characters in order to find these measurements. My method is to created a box at the top extension and take its Y size, then make a box at the bottom extension and take its Y size and use those values in the table. Visually:

Step 4: Use your font!
In your own script you can require the FontDisplayService and use :WriteFont to write your custom font on whatever GuiObject you want. Here’s some example code I threw together:

And the result:

What if you want characters that are represented by multiple characters in your text? That’s supported as well. Here I’m going to show you how to add a symbol to the Showcard font that appears as a red exclamation mark when used.

Step 1: Create your source image
You’ve already done this and so have I. Since I’m making an edit of the Showcard font I’m just going to edit its source image and add the red exclamation mark.

Step 2: Get your symbol’s dimensions and add them to the map
Believe it or not, you can simply write a multi-character character in the map. It’s really that easy. Get the dimensions the same way you normally would.

If you need to, you can also use extensions for this.

In this case, the specialWordCharactersList table may come into play. Since the is a punctuation symbol and I don’t want it to be split off from the word that it’s punctuating when the text wraps, I’m going to add it to the list.

Step 3: Use your new symbol
Writing a symbol is as easy as just including it in the input text. If I change my input text from my first scripting example to this…

This is example text<redexc>  Example text?  Example text!

Then the symbol will appear right after the word “text”.

If you guys make any cool stuff with this please post here and show everyone. Also, if you decide to make your own font post a link to the module and I’ll add it to the FontBin in my model so anyone who gets the custom font service will have your font as well.

Also, note that it would probably be wise to apply the pre-existing fonts to whatever uses you have in mind before creating your own font. That way if something breaks you won’t have spent 30 minutes creating a font map. Another benefit is that, if you test it with one of my fonts first, you’ll know it’s something you did in the module or your script if your font doesn’t work properly.

Enjoy!

7 Likes

Sounds awesome! :smiley:
I will experiment with it later…

Looks amazing! Is it able to update in realtime? Like if someone were to hook it up to a textbutton so it copied the text properties (textransparency and text), would it be able to keep up/work?

I’ve been waiting for you to release this, it is just simply amazing.

There’s no native support for that but, in theory, you could just generate new text (or alter the existing text) every time your textbutton changes. I’m not sure how well that would scale though, since there’s quite a few imagelabels involved. You’d probably be okay doing that for 10-50 characters but I wouldn’t trust the client’s machine to be able to keep up with anything more than that.

Oooh, nice. I’ll look into using this in my game.

I’ll definitely try this for one of my new games, thank you very much for sharing this with us! :DDD

I can just imagine of one us making a plugin for people who do not understand manually doing it.

Thanks.
I stick to good old Arial though.

You can also do high-res SurfaceGui text with this.