Font 2 PNG

License: Freeware
Version: 0.56 (from 7.10.2018 OV signed)
OS: Windows

Description

Poor man’s font 2 png.

Converts font file (ttf, otf, fon) to bitmap font as PNG file.

If “Fixed width” is unchecked the program produces in addition to the png-file a dat-file.

In the dat file there are two 4 byte integers for each character. The first one is starting position (x-coordinate) of a character in the bitmap font, the second is the width of the character in the bitmap font in pixels.

If the background color is saved, the font will be antialiased, otherwise it won’t. If you use black background color.

You can select characters to be save with ASCII range 32 – 128 or 0 – 255.

New in version 0.56

  • Each character can be centered in the given gap in the font
  • Some bugs fixed

Password to download: font2png

(It’s a zip archive)

Below is an example bitmap font of Cabin ttf-font produced by Font 2 PNG.

Instructions:

The program is quite self-explanatory.

When after converting the program asks filename, enter for example font as filename. This creates font.png and font.dat to use with the png-file.

Notice! In all the example programs below the ASCII range is 32 – 126; if range 0 – 255 is used the subtraction of 32 is not needed. When using the range of 0 – 255, remember also change the size of the arrays.

Example code to use font.png and font.dat files in Monkey X.

' Version 0.72

Import mojo
Import brl.stream
Import brl.filestream

Function Main()
	New MyApp
End

Class MyApp Extends App
	
	Const FONT_HEIGHT:Int = 32

	Global fontDat:Int[][]
	Global gfxFont:Image, gfxBG:Image
	
	Global width:Float,height:Float
	
	Method OnCreate()

		gfxFont = LoadImage("font.png")
		
		Local File:=FileStream.Open("monkey://data/font.dat","r")

		fontDat = allocateArray(94,2)
		
		For Local i:Int = 0 To 94 - 1
			fontDat[i][0] = File.ReadInt()
			fontDat[i][1] = File.ReadInt()
		Next
		
		File.Close()
		
		width = DeviceWidth()
		height = DeviceHeight()
	End
	
	Method OnUpdate()
		
	End
	
	Method OnRender()
		SetColor 0,200,0
		DrawRect 0,0,width,height
		
		SetBlend AdditiveBlend
		SetColor 255,255,0
		drawString("123ABC",0,0)
	End
	
	Function drawString(text:String, x, y)
		Local len:Int = text.Length()
		
		Local chrs:Int[]
		chrs = text.ToChars()
		
		For Local i:Int = 0 To len - 1
			DrawImageRect gfxFont, x, y, fontDat[chrs[i]-32][0], 0, fontDat[chrs[i]-32][1], FONT_HEIGHT
			x = x + fontDat[chrs[i]-32][1]
		Next

	End

        ' NEW! Added 07/19/2015
        ' Returns string's width in pixels
	Function stringWidth:Int(text:String)
		Local len:Int = text.Length()
		
		Local chrs:Int[]
		Local length:Int
		
		chrs = text.ToChars()
		
		For Local i:Int = 0 To len - 1
			length = length + fontDat[chrs[i]-32][1]
		Next
		
		Return length
	End

	Function allocateArray:Int[][](i:Int, j:Int)
    	Local arr:Int[][] = New Int[i][]
    	
    	For Local index = 0 Until i
    	    arr[index] = New Int[j]
    	Next
    	
    	Return arr		
	End

End Class

Constant FONT_HEIGHT must be inserted manually and is the height of the tallest character. In practice the height of the png-file.

Example code to use with mojo2 in Monkey X (with mojo2 fixed width bitmap fonts can be used):

Import mojo2

Function Main()
	New MyApp
End

Class MyApp Extends App

	Field canvas:Canvas
	Field font:Font
	
	Method OnCreate()
		canvas=New Canvas		
		font = Font.Load("font.png",32,94,true)
	End
	
	Method OnRender()
	
		canvas.Clear .7,0,.7
		
		canvas.SetFont font
		
		canvas.SetBlendMode 2
		canvas.SetColor 1,1,0,1
		canvas.DrawText "Sample text",DeviceWidth/2,DeviceHeight/2,.5,.5
		
		canvas.Flush
	End
End

Notice that if you’re for example working with Monkey X and try to save to “Game.data” folder new version of the font, the old contents can’t be overwritten. The font data must be saved elsewhere and then copied to “Game.data” folder.

Below is an example of use in a Java program with fixed width font:

javva window

The Java code:

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import javax.imageio.ImageIO;
import java.io.IOException;

public class Example {

	private JFrame frame;
	private ExamplePanel panel;
	
	public Example() {
		frame = new JFrame("Bitmap Font Example");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		initialize();
		frame.pack();
		frame.setVisible(true);
	}
	
	private void initialize() {
		panel = new ExamplePanel();
		frame.getContentPane().add(panel);
	}
	
	public static void main(String args[]) {
		new Example();
	}
}
	
class ExamplePanel extends JPanel {

	public BufferedImage gfxFont;
	
	public ExamplePanel() {

		try {
			gfxFont = ImageIO.read(new File("./font.png"));
		} catch (IOException e) {System.exit(0);}

		setBackground(Color.lightGray);
		setPreferredSize(new Dimension(500,200));
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		drawString(g,"Sample Text",60,60);
	}
	
	public void drawString(Graphics g, String text, int x, int y) {
		for (int i = 0; i < text.length(); i++) {
			// width of character is 34 pixels, height 56 pixels
			g.drawImage(gfxFont, x + i * 34, y , x + 34 * i + 34, y + 56, (text.charAt(i)-32) * 34, 0, (text.charAt(i)-32) * 34 + 34,56,null);
		}	
	}
	

}

And then example of non-fixed width usage:javva-example

The code:

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Example {

	private JFrame frame;
	private ExamplePanel panel;
	
	public Example() {
		frame = new JFrame("Bitmap Font Example");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		initialize();
		frame.pack();
		frame.setVisible(true);
	}
	
	private void initialize() {
		panel = new ExamplePanel();
		frame.getContentPane().add(panel);
	}
	
	public static void main(String args[]) {
		new Example();
	}
}
	
class ExamplePanel extends JPanel {

	public BufferedImage gfxFont;
	public int[][] fontData = new int[94][2];
	
	public ExamplePanel() {

		try {
			gfxFont = ImageIO.read(new File("./font.png"));
		} catch (IOException e) {System.exit(0);}

		
		DataInputStream inp = null;
		try {
			inp = new DataInputStream(new BufferedInputStream(new FileInputStream("./font.dat"),  752));
		} catch (IOException e) {System.exit(0);}

		byte[] buffer = new byte[4];
		ByteBuffer byteBuffer = ByteBuffer.allocate(4);

		try {
			for (int j=0; j < 94; j++) {
				for (int i=0; i < 2; i++) {
					inp.read(buffer);
					byteBuffer = ByteBuffer.wrap(buffer);
					fontData[j][i] = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).getInt();
				}
			}
		} catch (IOException e) {System.exit(0);}
		finally {
            try {
                if (inp != null)
                    inp.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
		
		setBackground(Color.lightGray);
		setPreferredSize(new Dimension(500,200));
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		drawString(g,"Sample Text",60,60);
	}
	
	public void drawString(Graphics g, String text, int x, int y) {
		int dx = 0;
		
		for (int i = 0; i < text.length(); i++) {
			// height is 74 pixels;
			dx = fontData[text.charAt(i)-32][1];
			g.drawImage(gfxFont, x, y,x + dx, y + 74, fontData[(text.charAt(i)-32)][0], 0, fontData[(text.charAt(i)-32)][0] + fontData[text.charAt(i)-32][1],74,null);
			x = x + dx;
		}	
	}
	

}

Update!

The Monkey X code below works on Android systems:

Import mojo
Import brl.databuffer

Function Main()
	New MyApp
End

Class MyApp Extends App
	
	Const FONT_HEIGHT:Int = 37

	Global fontDat:DataBuffer
	Global gfxFont:Image
	
	Method OnCreate()

		gfxFont = LoadImage("font.png")
		fontDat = New DataBuffer(95*4*2) ' 95 characters, for each character two 4 byte integers
		fontDat = DataBuffer.Load("monkey://data/font.dat")
		
	End
	
	Method OnUpdate()
		
	End
	
	Method OnRender()
		PushMatrix()
		Scale(DeviceWidth()/800.0,DeviceHeight()/600.0)
		Cls
		
		SetBlend AdditiveBlend
		SetColor 255,255,0
		drawString("123ABC",(800 - stringWidth("123ABC")) / 2, (600 - 37) / 2)
		
		PopMatrix()	
	End
		
	Function drawString(text:String, x, y)
		Local len = text.Length()
		
		Local chrs:Int[]
		chrs = text.ToChars()
		
		For Local i = 0 To len - 1 
			DrawImageRect gfxFont, x, y, fontDat.PeekInt((chrs[i]-32)*4*2), 0, fontDat.PeekInt((chrs[i]-32)*4*2 + 4), FONT_HEIGHT
			x = x + fontDat.PeekInt((chrs[i]-32)*4*2 + 4)
		Next

	End

	Function stringWidth:Int(text:String)
		Local len:Int = text.Length()
		Local chrs:Int[]
		Local length:Int = 0
		
		chrs = text.ToChars()
		
		For Local i:Int = 0 To len - 1
			length = length + fontDat.PeekInt((chrs[i]-32)*4*2+4)
		Next
		
		Return length
	End

End Class

Feel free to use these codes.

I have a blog post regarding simple text scrollers in Monkey X (and Monkey2). The Monkey X version uses data files of Font 2 PNG, that can be downloaded. Good example in full how to use the files of Font 2 PNG.

See this and this blog post for code to scale a font made with Font 2 PNG in Monkey X. At the end of the posts is a video demonstration of the codes.

I’ve implemented also two examples of own font class to use with Font 2 PNG in Monkey X:

Here’s also a font class for Cerberus X:

I’ve used Font 2 PNG’s fonts in my Old School demonstrations like in the following: