Skip to content

Font Support

Jinny You edited this page Mar 23, 2026 · 7 revisions

Default Font Embedding

ThorVG Web adopts default font, enabling essential glyph rendering even in environments where no font is explicitly loaded.

Default Font Information

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +-=().

Embedding tool

A tool for embedding the default font into the WASM binary. Converts a TTF file into a C++ header with LZSS-compressed data.

Source Code (font_to_hex.py)

Show code
import sys

def lzss_encode(data):
    if not data:
        return []

    window_size = 4095
    min_match = 3
    max_match = 18
    
    output = []
    pos = 0
    buffer = bytearray(data)
    
    while pos < len(buffer):
        flags = 0
        flag_pos = len(output)
        output.append(0)
        
        for flag_bit in range(8):
            if pos >= len(buffer):
                break

            best_length = 0
            best_offset = 0
            search_start = max(0, pos - window_size)
            
            for i in range(search_start, pos):
                length = 0
                while (pos + length < len(buffer) and 
                       length < max_match and 
                       buffer[i + length] == buffer[pos + length]):
                    length += 1
                
                if length > best_length:
                    best_length = length
                    best_offset = pos - i
            
            if best_length >= min_match:
                offset_high = (best_offset >> 4) & 0xFF
                offset_low = ((best_offset & 0x0F) << 4) | ((best_length - min_match) & 0x0F)
                output.append(offset_high)
                output.append(offset_low)
                pos += best_length
            else:
                flags |= (1 << flag_bit)
                output.append(buffer[pos])
                pos += 1
                
        output[flag_pos] = flags
    
    return output

def convert_to_hex(input_file, output_file, array_name="embedded_font"):
    with open(input_file, "rb") as f:
        data = f.read()

    compressed_data = lzss_encode(data)
    hex_array = ", ".join(f"0x{b:02X}" for b in compressed_data)
    compressed_size = len(compressed_data)
    original_size = len(data)

    cpp_content = f"""#pragma once

#include <cstddef>

constexpr unsigned char {array_name}_compressed[] = {{
    {hex_array}
}};

constexpr size_t {array_name}_compressed_size = {compressed_size};
constexpr size_t {array_name}_original_size = {original_size};

inline void decompress_{array_name}(unsigned char* output) {{
    const unsigned char* input = {array_name}_compressed;
    size_t input_pos = 0;
    size_t output_pos = 0;
    
    while (input_pos < {array_name}_compressed_size && output_pos < {array_name}_original_size) {{
        unsigned char flags = input[input_pos++];
        
        for (int bit = 0; bit < 8 && output_pos < {array_name}_original_size; bit++) {{
            if (flags & (1 << bit)) {{
                output[output_pos++] = input[input_pos++];
            }} else {{
                unsigned short offset = input[input_pos++] << 4;
                unsigned char length_offset = input[input_pos++];
                offset |= length_offset >> 4;
                unsigned char length = (length_offset & 0x0F) + 3;
                
                size_t copy_pos = output_pos - offset;
                for (int i = 0; i < length; i++) {{
                    output[output_pos++] = output[copy_pos + i];
                }}
            }}
        }}
    }}
}}

"""

    with open(output_file, "w") as f:
        f.write(cpp_content)

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: font_to_hex.py <input_font_file> <output_cpp_file>")
        sys.exit(1)

    input_font = sys.argv[1]
    output_cpp = sys.argv[2]
    convert_to_hex(input_font, output_cpp)

Usage

# Extract glyphs (fonttools)
$ pyftsubset DMSans-Regular.ttf \
    --text="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +-=()." \
    --output-file=DMSans-subset.ttf

# Convert to code
$ python3 font_to_hex.py <input.ttf> <output.h>

# Replace wasm/common/tvgWasmDefaultFont.h with the generated header data.

Clone this wiki locally