Module x.ttf stdlib

x.ttf
Version:
0.3.3
License:
MIT
Dependencies from vmod:
0
Imports:
7
Imported by:
1
Repository:
OS-specific
Show selected OS-specific symbols.
Backend-specific
Show selected Backend-specific symbols.

Dependencies defined in v.mod

This section is empty.

Imports

Imported by

Overview

introduction

This module is designed to perform two main task

  • Load the font file
  • Render text using a TTF font

The render system can be single or multiple, for example it is possible to have a bitmap render and a HW accelerated render.

TTF loader

This part of the module do a simple task, load a TTF file and preprocess all the loaded data in order to simplify the rendering phase.

Let's start with a simple snippet of code that load a font from the disk:

mut ttf_font := ttf.TTF_File{}
ttf_font.buf = os.read_bytes("arial.ttf") or { panic(err) }
ttf_font.init()

The font must be passed to the TTF_file as RAM buffer.

At this point the font "arial" is loaded and parsed and if it is a valid TTF font it is ready for the rendering. We can get some quick info on the font as string using the get_info_string function:

println(ttf_font.get_info_string())

produces an output like this:

----- Font Info -----
font_family     : Arial
font_sub_family : Normal
full_name       : Arial
postscript_name : ArialMT
version         : 1
font_revision   : 5.06
magic_number    : 5f0f3cf5
flags           : 81b
created  unixTS : 649950890
modified unixTS : 1282151447
units_per_em    : 2048
box             : [x_min:-1361, y_min:-665, x_Max:4096, y_Max:2060]
mac_style       : 0
-----------------------

Once loaded a font the TTF_File struct is filled with the font data and texts can be rendered. At high level no more action are required to use the loaded font. Multiple fonts can be loaded without problems at the same time.

TTF Bitmap render

In this module it is possible to have different renders running at the same time. At the present time all the rendering are made on the CPU, sokol is used only to draw the rendered text to the screen. Let's start with a simple snippet of code:

import os
import x.ttf

fn main() {
    mut ttf_font := ttf.TTF_File{}
    ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
    ttf_font.init()
    // print font info
    println(ttf_font.get_info_string())
}

This simple code load a TTF font and display its basic information.

draw_text

The draw text function draw simple strings without indentation or other imagination tasks. At this point we can render a simple text:

import os
import x.ttf

fn main() {
    mut ttf_font := ttf.TTF_File{}
    ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
    ttf_font.init()
    // print font info
    println(ttf_font.get_info_string())

    bmp_width := 200
    bmp_heigth := 64
    bmp_layers := 4 // number of planes for an RGBA buffer
    // memory size of the buffer
    bmp_size := bmp_width * bmp_heigth * bmp_layers

    font_size := 32 // font size in points
    device_dpi := 72 // default screen DPI
    // Formula for scale calculation
    // scaler := (font_size * device dpi) / (72dpi * em_unit)
    scale := f32(font_size * device_dpi) / f32(72 * ttf_font.units_per_em)
    // height of the font to use in the buffer to separate the lines
    y_base := int((ttf_font.y_max - ttf_font.y_min) * scale)

    // declare the bitmap struct
    mut bmp := ttf.BitMap{
        tf: &ttf_font
        buf: malloc(bmp_size)
        buf_size: bmp_size
        width: bmp_width
        height: bmp_heigth
        bp: bmp_layers
        color: 0x000000_FF // RGBA black
        scale: scale
    }
    bmp.init_filler()
    bmp.clear()
    bmp.set_pos(10, y_base)
    bmp.draw_text('Test Text!')
    bmp.save_as_ppm('test.ppm')
}

This is the low level render that draw the text on a bitmap and save the bitmap on a disk as .ppm file.

The render in this case is a raw rendering without any postfiltering or other processing.

Using the low level rendering you need to manage all the amenities like allocate and release memory and other tasks like calc the character dimensions.

You can specify the style for the text rendering in the BitMap struct::

enum Style {
    outline
    outline_aliased
    filled // default style
    raw
}

Use this level only if you want achieve particular result on text rendering.

draw_text_block

Draw text block draw a justified and indented block of multiline text in the bitmap.

import os
import x.ttf

fn main() {
    mut ttf_font := ttf.TTF_File{}
    ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
    ttf_font.init()
    // print font info
    println(ttf_font.get_info_string())

    bmp_width := 200
    bmp_heigth := 200
    bmp_layers := 4 // number of planes for an RGBA buffer
    // memory size of the buffer
    bmp_size := bmp_width * bmp_heigth * bmp_layers

    font_size := 32 // font size in points
    device_dpi := 72 // default screen DPI
    // Formula for scale calculation
    // scaler := (font_size * device dpi) / (72dpi * em_unit)
    scale := f32(font_size * device_dpi) / f32(72 * ttf_font.units_per_em)
    // height of the font to use in the buffer to separate the lines
    y_base := int((ttf_font.y_max - ttf_font.y_min) * scale)

    text := "Today it is a good day!
Tomorrow I'm not so sure :(
But Vwill prevail for sure, V is the way!!
òàèì@ò!£$%&
"
    // declare the bitmap struct
    mut bmp := ttf.BitMap{
        tf: &ttf_font
        buf: malloc(bmp_size)
        buf_size: bmp_size
        width: bmp_width
        height: bmp_heigth
        bp: bmp_layers
        color: 0x000000_FF // RGBA black
        scale: scale
    }
    bmp.init_filler()
    bmp.clear()
    bmp.justify = true
    bmp.align = .left
    bmp.draw_text_block(text, x: 0, y: 0, w: bmp_width - 20, h: bmp_heigth)
    bmp.save_as_ppm('test.ppm')
}

This is the low level render that draw text block on the bitmap. A text block is defined from a Text_block struct:

struct Text_block {
    x         int  // x position of the left high corner
    y         int  // y position of the left high corner
    w         int  // width of the text block
    h         int  // height of the text block
    cut_lines bool = true // force to cut the line if the length is over the text block width
}

and use the following bitmap fields:

    style              Style      = .filled // default style
    align              Text_align = .left   // default text align
    justify            bool				    // justify text flag, default deactivated
    justify_fill_ratio f32        = 0.5     // justify fill ratio, if the ratio of the filled
                                            // row is >= of this then justify the text

It is possible to modify these parameters to obtain the desired effect on the text rendering.

TTF Sokol render

The sokol render use the bitmap render to create the text and the gg functions to render the text to the screen. It is simpler to use in a gg app than the raw bitmap render. Each single text rendered need its own reder to be declared, after you can modify it. Here a simple example of the usage:

import gg
import gx
import sokol.sapp
import sokol.sgl
import sokol.gfx
import x.ttf
import os

const (
    win_width  = 600
    win_height = 700
    bg_color   = gx.white
    font_paths = [
        'arial.ttf',
    ]
)

struct App_data {
pub mut:
    gg        &gg.Context
    sg_img    gfx.Image
    init_flag bool
    frame_c   int

    tf         []ttf.TTF_File
    ttf_render []ttf.TTF_render_Sokol
}

fn my_init(mut app App_data) {
    app.init_flag = true
}

fn draw_frame(mut app App_data) {
    cframe_txt := 'Current Frame: ${app.frame_c}'

    app.gg.begin()

    sgl.defaults()
    sgl.matrix_mode_projection()
    sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0)

    // draw text only if the app is already initialized
    if app.init_flag == true {
        // update the text
        mut txt1 := &app.ttf_render[0]
        txt1.destroy_texture()
        txt1.create_text(cframe_txt, 43)
        txt1.create_texture()
        txt1.draw_text_bmp(app.gg, 30, 60)
    }
    app.frame_c++
    app.gg.end()
}

fn main() {
    mut app := &App_data{
        gg: 0
    }

    app.gg = gg.new_context(
        width: win_width
        height: win_height
        create_window: true
        window_title: 'Test TTF module'
        user_data: app
        bg_color: bg_color
        frame_fn: draw_frame
        init_fn: my_init
    )

    // load TTF fonts
    for font_path in font_paths {
        mut tf := ttf.TTF_File{}
        tf.buf = os.read_bytes(font_path) or { panic(err) }
        println('TrueTypeFont file [${font_path}] len: ${tf.buf.len}')
        tf.init()
        println(tf.get_info_string())
        app.tf << tf
    }

    // TTF render 0 Frame counter
    app.ttf_render << &ttf.TTF_render_Sokol{
        bmp: &ttf.BitMap{
            tf: &app.tf[0]
            buf: unsafe { malloc(32000000) }
            buf_size: (32000000)
            color: 0xFF0000FF
            // style: .raw
        }
    }

    app.gg.run()
}

Aliases

This section is empty.

Constants

This section is empty.

Sum types

This section is empty.

Functions

#fn color_multiply

inline
fn color_multiply(c u32, level f32) u32

#fn color_multiply_alpha

inline
fn color_multiply_alpha(c u32, level f32) u32

Structs

#struct BitMap

pub struct BitMap {
pub mut:
	tf       &TTF_File = unsafe { nil }
	buf      &u8       = unsafe { nil } // pointer to the memory buffer
	buf_size int // allocated buf size in bytes
	width    int = 1 // width of the buffer
	height   int = 1 // height of the buffer
	bp       int = 4 // byte per pixel of the buffer
	bg_color u32 = 0xFFFFFF_00 // background RGBA format
	color    u32 = 0x000000_FF // RGBA format
	scale    f32 = 1.0 // internal usage!!
	scale_x  f32 = 1.0 // X scale of the single glyph
	scale_y  f32 = 1.0 // Y scale of the single glyph
	angle    f32 = 0.0 // angle of rotation of the bitmap
	// spaces
	space_cw   f32 = 1.0 // width of the space glyph internal usage!!
	space_mult f32 = f32(0.0) // 1.0/16.0  // space between letter, is a multiplier for a standrd space ax
	// used only by internal text rendering!!
	tr_matrix          []f32      = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // transformation matrix
	ch_matrix          []f32      = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // character matrix
	style              Style      = .filled // default syle
	align              Text_align = .left // default text align
	justify            bool // justify text flag, default deactivated
	justify_fill_ratio f32 = 0.5 // justify fill ratio, if the ratio of the filled row is >= of this then justify the text
	filler             [][]int    // filler buffer for the renderer
	// flag to force font embedded metrics
	use_font_metrics bool
}

#fn (&BitMap) format_texture

fn (mut bmp &BitMap) format_texture()

transform the bitmap from one layer to color layers

#fn (&BitMap) save_as_ppm

fn (mut bmp &BitMap) save_as_ppm(file_name string)

write out a .ppm file

#fn (&BitMap) get_raw_bytes

fn (mut bmp &BitMap) get_raw_bytes() []u8

#fn (&BitMap) save_raw_data

fn (mut bmp &BitMap) save_raw_data(file_name string)

#fn (&BitMap) clear

fn (mut bmp &BitMap) clear()

clear clear the bitmap with 0 bytes

#fn (&BitMap) trf_txt

fn (bmp &BitMap) trf_txt(p &Point) (int, int)

transform matrix applied to the text

#fn (&BitMap) trf_ch

fn (bmp &BitMap) trf_ch(p &Point) (int, int)

transform matrix applied to the char

#fn (&BitMap) set_pos

fn (mut bmp &BitMap) set_pos(x f32, y f32)

set draw postion in the buffer

#fn (&BitMap) set_rotation

fn (mut bmp &BitMap) set_rotation(a f32)

set the rotation angle in radiants

#fn (&BitMap) init_filler

fn (mut bmp &BitMap) init_filler()

#fn (&BitMap) clear_filler

fn (mut bmp &BitMap) clear_filler()

#fn (&BitMap) exec_filler

fn (mut bmp &BitMap) exec_filler()

#fn (&BitMap) fline

fn (mut bmp &BitMap) fline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32)

#fn (&BitMap) plot

inline
fn (mut bmp &BitMap) plot(x int, y int, c u32) bool

#fn (&BitMap) aline

fn (mut bmp &BitMap) aline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32)

aline draw an aliased line on the bitmap

#fn (&BitMap) line

fn (mut bmp &BitMap) line(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32)

#fn (&BitMap) box

fn (mut bmp &BitMap) box(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32)

#fn (&BitMap) quadratic

fn (mut bmp &BitMap) quadratic(in_x0 int, in_y0 int, in_x1 int, in_y1 int, in_cx int, in_cy int, c u32)

#fn (&BitMap) get_chars_bbox

fn (mut bmp &BitMap) get_chars_bbox(in_string string) []int

#fn (&BitMap) get_bbox

fn (mut bmp &BitMap) get_bbox(in_string string) (int, int)

#fn (&BitMap) draw_notdef_glyph

fn (mut bmp &BitMap) draw_notdef_glyph(in_x int, in_w int)

#fn (&BitMap) draw_text

fn (mut bmp &BitMap) draw_text(in_string string) (int, int)

#fn (&BitMap) draw_glyph

fn (mut bmp &BitMap) draw_glyph(index u16) (int, int)

#fn (&BitMap) get_justify_space_cw

fn (mut dev &BitMap) get_justify_space_cw(txt string, w int, block_w int, space_cw int) f32

#fn (&BitMap) draw_text_block

fn (mut bmp &BitMap) draw_text_block(text string, block Text_block)

write out a text

#struct TTF_render_Sokol

pub struct TTF_render_Sokol {
pub mut:
	bmp &BitMap = unsafe { nil } // Base bitmap render
	// rendering fields
	sg_img       gfx.Image // sokol image
	scale_reduct f32 = 2.0 // scale of the cpu texture for filtering
	device_dpi   int = 72 // device DPI
}

#fn (&TTF_render_Sokol) format_texture

fn (mut tf_skl &TTF_render_Sokol) format_texture()

#fn (&TTF_render_Sokol) create_text

fn (mut tf_skl &TTF_render_Sokol) create_text(in_txt string, in_font_size f32)

#fn (&TTF_render_Sokol) create_text_block

fn (mut tf_skl &TTF_render_Sokol) create_text_block(in_txt string, in_w int, in_h int, in_font_size f32)

#fn (&TTF_render_Sokol) create_texture

fn (mut tf_skl &TTF_render_Sokol) create_texture()

#fn (TTF_render_Sokol) destroy_texture

fn (tf_skl TTF_render_Sokol) destroy_texture()

#fn (&TTF_render_Sokol) update_text_texture

fn (mut tf_skl &TTF_render_Sokol) update_text_texture()

Use only if usage: .dynamic

#fn (TTF_render_Sokol) draw_text_bmp

fn (tf_skl TTF_render_Sokol) draw_text_bmp(ctx &gg.Context, x f32, y f32)

#struct Text_block

pub struct Text_block {
	x         int  // x postion of the left high corner
	y         int  // y postion of the left high corner
	w         int  // width of the text block
	h         int  // heigth of the text block
	cut_lines bool = true // force to cut the line if the length is over the text block width
}

#struct TTF_File

pub struct TTF_File {
pub mut:
	buf                     []u8
	pos                     u32
	length                  u16
	scalar_type             u32
	search_range            u16
	entry_selector          u16
	range_shift             u16
	tables                  map[string]Offset_Table
	version                 f32
	font_revision           f32
	checksum_adjustment     u32
	magic_number            u32
	flags                   u16
	units_per_em            u16
	created                 u64
	modified                u64
	x_min                   f32
	y_min                   f32
	x_max                   f32
	y_max                   f32
	mac_style               u16
	lowest_rec_ppem         u16
	font_direction_hint     i16
	index_to_loc_format     i16
	glyph_data_format       i16
	font_family             string
	font_sub_family         string
	full_name               string
	postscript_name         string
	cmaps                   []TrueTypeCmap
	ascent                  i16
	descent                 i16
	line_gap                i16
	advance_width_max       u16
	min_left_side_bearing   i16
	min_right_side_bearing  i16
	x_max_extent            i16
	caret_slope_rise        i16
	caret_slope_run         i16
	caret_offset            i16
	metric_data_format      i16
	num_of_long_hor_metrics u16
	kern                    []Kern0Table
	// panose
	panose_array []u8 = []u8{len: 12, init: 0}
	// cache
	glyph_cache map[int]Glyph
	// font widths array scale for PDF export
	width_scale f32 = 1.0
}

#fn (&TTF_File) init

fn (mut tf &TTF_File) init()

#fn (&TTF_File) get_horizontal_metrics

fn (mut tf &TTF_File) get_horizontal_metrics(glyph_index u16) (int, int)

#fn (&TTF_File) get_glyph_offset

fn (mut tf &TTF_File) get_glyph_offset(index u32) u32

#fn (&TTF_File) glyph_count

fn (mut tf &TTF_File) glyph_count() u16

#fn (&TTF_File) read_glyph_dim

fn (mut tf &TTF_File) read_glyph_dim(index u16) (int, int, int, int)

#fn (&TTF_File) get_ttf_widths

fn (mut tf &TTF_File) get_ttf_widths() ([]int, int, int)

#fn (&TTF_File) read_glyph

fn (mut tf &TTF_File) read_glyph(index u16) Glyph

#fn (&TTF_File) read_simple_glyph

fn (mut tf &TTF_File) read_simple_glyph(mut in_glyph &Glyph)

#fn (&TTF_File) read_compound_glyph

fn (mut tf &TTF_File) read_compound_glyph(mut in_glyph &Glyph)

#fn (&TTF_File) get_u8

fn (mut tf &TTF_File) get_u8() u8

#fn (&TTF_File) get_i8

fn (mut tf &TTF_File) get_i8() i8

#fn (&TTF_File) get_u16

fn (mut tf &TTF_File) get_u16() u16

#fn (&TTF_File) get_ufword

fn (mut tf &TTF_File) get_ufword() u16

#fn (&TTF_File) get_i16

fn (mut tf &TTF_File) get_i16() i16

#fn (&TTF_File) get_fword

fn (mut tf &TTF_File) get_fword() i16

#fn (&TTF_File) get_u32

fn (mut tf &TTF_File) get_u32() u32

#fn (&TTF_File) get_i32

fn (mut tf &TTF_File) get_i32() int

#fn (&TTF_File) get_2dot14

fn (mut tf &TTF_File) get_2dot14() f32

#fn (&TTF_File) get_fixed

fn (mut tf &TTF_File) get_fixed() f32

#fn (&TTF_File) get_string

fn (mut tf &TTF_File) get_string(length int) string

#fn (&TTF_File) get_unicode_string

fn (mut tf &TTF_File) get_unicode_string(length int) string

#fn (&TTF_File) get_date

fn (mut tf &TTF_File) get_date() u64

#fn (&TTF_File) calc_checksum

fn (mut tf &TTF_File) calc_checksum(offset u32, length u32) u32

#fn (&TTF_File) read_offset_tables

fn (mut tf &TTF_File) read_offset_tables()

#fn (&TTF_File) read_head_table

fn (mut tf &TTF_File) read_head_table()

#fn (&TTF_File) read_name_table

fn (mut tf &TTF_File) read_name_table()

#fn (&TTF_File) read_cmap_table

fn (mut tf &TTF_File) read_cmap_table()

#fn (&TTF_File) read_cmap

fn (mut tf &TTF_File) read_cmap(offset u32)

#fn (&TTF_File) map_code

fn (mut tf &TTF_File) map_code(char_code int) u16

#fn (&TTF_File) read_hhea_table

fn (mut tf &TTF_File) read_hhea_table()

#fn (&TTF_File) create_kern_table0

fn (mut tf &TTF_File) create_kern_table0(vertical bool, cross bool) Kern0Table

#fn (&TTF_File) read_kern_table

fn (mut tf &TTF_File) read_kern_table()

#fn (&TTF_File) reset_kern

fn (mut tf &TTF_File) reset_kern()

#fn (&TTF_File) next_kern

fn (mut tf &TTF_File) next_kern(glyph_index int) (int, int)

#fn (&TTF_File) read_panose_table

fn (mut tf &TTF_File) read_panose_table()

#fn (&TTF_File) get_info_string

fn (tf &TTF_File) get_info_string() string

#struct Point

pub struct Point {
pub mut:
	x        int
	y        int
	on_curve bool
}

#struct Glyph

pub struct Glyph {
pub mut:
	g_type             u16 = ttf.g_type_simple
	contour_ends       []u16
	number_of_contours i16
	points             []Point
	x_min              i16
	x_max              i16
	y_min              i16
	y_max              i16
	valid_glyph        bool
	components         []Component
}

Interfaces

This section is empty.

Enums

#enum Text_align

pub enum Text_align {
	left
	center
	right
	justify
}

text align

#enum Style

pub enum Style {
	outline
	outline_aliased
	filled
	raw
}

draw style