cali

May 28, 2023 · View on GitHub

cali

A calligraphic library for fonts

Library usage

Load a font from a ttf file:

use cali_m
type(ttf_t)  :: ttf
ttf  = read_ttf('./fonts/computer-modern/cmunrm.ttf')  ! roman

Set the foreground and background colors, e.g. using z'RRGGBBAA' format:

integer(kind = 4) :: fg, bg
! [... other declarations and code omitted]
fg = new_color(int(z'000000ff',8))
bg = new_color(int(z'e8e6cbff',8))

Set the resolution, line spacing, and margin:

double precision :: pixels_per_em
integer :: line_height, left_margin
! [...]
pixels_per_em = 100.d0
line_height   = nint(1.2 * pixels_per_em)
left_margin   = 20

Make a canvas of pixels with a width, height, and background color:

integer(kind = 4), allocatable :: canvas(:,:)
! [...]
canvas = new_canvas(700, 400, bg)

Draw some strings of text on the canvas:

call draw_str(canvas, fg, ttf, "Hello, world!", &
	left_margin, 1 * line_height, pixels_per_em)

call draw_str(canvas, fg, ttf, "föø, бар, βαζ", &
	left_margin, 2 * line_height, pixels_per_em)

Strings must be encoded in UTF-8. If you have a UTF-32 string, there is a helper function to_utf8(str32) that you can use to convert it. UTF-16 is not supported.

Here, the x position of the text is at the left margin, and the y position is at multiples of the line height. You can mix and match different fonts (e.g. regular and italic) and styles within the same canvas by changing these arguments for each typeset string.

At this point, you can do whatever you want with the pixel canvas. For example, export the canvas to a ppm image file:

call write_img(canvas, 'my-file.ppm')

Theoretically you could display the canvas through a graphics library like SDL, OpenGL, or others if you have a Fortran interface for that.

A whole program could look like this:

program main

	use cali_m

	implicit none

	double precision :: pixels_per_em

	integer :: line_height, left_margin
	integer(kind = 4) :: fg, fg2, bg
	integer(kind = 4), allocatable :: canvas(:,:)

	type(ttf_t)  :: ttf, ttfi

	ttf  = read_ttf('./fonts/computer-modern/cmunrm.ttf')  ! roman
	ttfi = read_ttf('./fonts/computer-modern/cmunti.ttf')  ! italic

	! foreground/background colors
	fg  = new_color(int(z'000000ff',8))
	fg2 = new_color(int(z'2a7fffff',8))
	bg  = new_color(int(z'e8e6cbff',8))

	canvas = new_canvas(700, 400, bg)

	pixels_per_em = 100.d0
	line_height   = nint(1.2 * pixels_per_em)
	left_margin   = 20

	call draw_str(canvas, fg , ttf , "Hello, world!", &
		left_margin, 1 * line_height, pixels_per_em)

	call draw_str(canvas, fg2, ttfi, "föø, бар, βαζ", &
		left_margin, 2 * line_height, pixels_per_em)

	call write_img(canvas, 'my-file.ppm')

end program main

The typeset image looks like this:

Linking

Use CMake

Details TBD. See CMakeLists.txt.

Resources

My YouTube playlist of development streams for this library: https://youtube.com/playlist?list=PLkNcKcm8wEj62mKiE7yVUFkmClTYpdpCK

Apple documentation on the ttf glyf (glyph) table: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html

Online ttf preview tool: fontdrop.info

Steve Hanov's blog on parsing ttf files in JavaScript: http://stevehanov.ca/blog/?id=143

Article about on-curve vs off-curve points for TrueType outlines: https://www.truetype-typography.com/ttoutln.htm

Freya Holmér's video on splines: https://www.youtube.com/watch?v=jvPPXbo87ds

UTF-8 transcoding: https://rosettacode.org/wiki/UTF-8_encode_and_decode

Tsoding's olivec graphics library demos, which partially inspired the architecture for this library: https://tsoding.github.io/olive.c/