A high-performance text rendering engine for the V programming language, built on Pango, HarfBuzz, FreeType, and Sokol.
VGlyph provides production-grade text layout—including bidirectional text, complex scripts, and rich text markup—while remaining easy to use.

TextSystem,
TextConfig, etc.vglyph with screen readers.<span> tags for colors, fonts, and styles within a single
string..ttf / .otf files directly from your assets folder.TextConfig.wght) and width
(wdth) for fluid typography animations.text_width, text_height,
font_height) for precise layout calculations.VGlyph supports CJK IME for Japanese, Chinese, and Korean input.
macOS overlay API (v1.8+):
$if darwin {
ns_window := C.sapp_macos_get_window()
ime_overlay := vglyph.ime_overlay_create_auto(ns_window)
vglyph.ime_overlay_register_callbacks(ime_overlay,
on_marked_text, on_insert_text,
on_do_command, on_get_rect, on_clause, user_data)
vglyph.ime_overlay_set_focused_field(ime_overlay, 'field_id')
}
Cross-platform global callbacks (v1.3+):
For simpler single-field apps, global callbacks work on all platforms.
See EDITING.md for API details and IME-APPENDIX.md for CJK IME architecture.
Known issue: Korean first-keypress macOS bug (QTBUG-136128, FB17460926).
You must have Pango and FreeType installed.
brew install pango freetype pkg-config
sudo apt-get install libpango1.0-dev libfreetype6-dev pkg-config
# For comprehensive emoji and multilingual support
sudo apt-get install fonts-noto fonts-noto-color-emoji
# vcpkg
vcpkg install pango freetype
# Or MSYS2
pacman -S mingw-w64-x86_64-pango mingw-w64-x86_64-freetype
Use the TextSystem for the easiest integration. It handles initialization,
caching, and rendering.
import vglyph
import gg
struct App {
mut:
gg &gg.Context = unsafe { nil }
ts &vglyph.TextSystem = unsafe { nil }
}
fn main() {
mut app := &App{}
app.gg = gg.new_context(
bg_color: gg.white
width: 800
height: 600
window_title: 'VGlyph Demo'
init_fn: init
frame_fn: frame
user_data: app
)
app.gg.run()
}
fn init(mut app App) {
// 1. Initialize TextSystem
app.ts = vglyph.new_text_system(mut app.gg) or { panic(err) }
}
fn frame(mut app App) {
app.gg.begin()
// 2. Draw Text
// Coordinates are (x, y)
app.ts.draw_text(100, 100, 'Hello VGlyph!', vglyph.TextConfig{
style: vglyph.TextStyle{
font_name: 'Sans Bold 30'
color: gg.black
}
}) or { println(err) }
app.gg.end()
// 3. Commit Texture Uploads (Important!)
app.ts.commit()
}
The examples/ directory contains several demonstrations:
demo.v - Multilingual text, wrapping, and rich text markupemoji_demo.v - Emoji and color bitmap renderingeditor_demo.v - Interactive text editing with CJK IME supporttypography_demo.v - OpenType features and custom tab stopsvariable_font_demo.v - Variable font animation (weight/width axes)list_demo.v - Unordered and ordered lists with hanging indentsrotate_text.v - Rotated text with animated angletransform_text.v - Matrix transform text (rotate + translate + skew)stress_demo.v - Performance testing with thousands of glyphsRun any example with:
v run examples/demo.v
width := app.ts.text_width('Hello', cfg)!
height := app.ts.text_height('Hello', cfg)!
font_h := app.ts.font_height(cfg)
// Check which font Pango actually resolved
actual_font := app.ts.resolve_font_name('Sans Bold 30')
println('Using font: ${actual_font}')
// Get layout for hit testing or custom rendering
layout := app.ts.layout_text('Click me', cfg)!
char_idx := layout.hit_test(mouse_x, mouse_y)
rects := layout.get_selection_rects(0, 5)
layout := app.ts.layout_text('Affine Text', cfg)!
t := vglyph.AffineTransform{
xx: 1.0
xy: 0.25 // skew X
yx: 0.0
yy: 1.0
x0: 20.0 // local translation
y0: 0.0
}
app.ts.draw_layout_transformed(layout, 200, 180, t)
// Import vglyph
rt := vglyph.RichText{
runs: [
vglyph.StyleRun{ text: 'Hello ' },
vglyph.StyleRun{
text: 'World',
style: vglyph.TextStyle{
color: gg.red,
underline: true
}
}
]
}
layout := app.ts.layout_rich_text(rt, cfg)!
app.ts.draw_layout(layout, x, y)
Ensure you're calling commit() at the end of each frame. Without it, new
glyphs won't upload to the GPU.
Install comprehensive fallback fonts:
sudo apt-get install fonts-noto fonts-noto-color-emojiUse the font's family name, not the filename. Check with:
println(app.ts.resolve_font_name('YourFont'))
TextSystem automatically caches layouts. If text changes every frame (e.g.,
FPS counter), consider using a monospace font and fixed-width formatting to
minimize layout variations.
MIT
V
84.1%
C
9.2%
Objective-C
6.7%
A high-performance text rendering engine for the V programming language, built on Pango, HarfBuzz, FreeType, and Sokol.
VGlyph provides production-grade text layout—including bidirectional text, complex scripts, and rich text markup—while remaining easy to use.

TextSystem,
TextConfig, etc.vglyph with screen readers.<span> tags for colors, fonts, and styles within a single
string..ttf / .otf files directly from your assets folder.TextConfig.wght) and width
(wdth) for fluid typography animations.text_width, text_height,
font_height) for precise layout calculations.VGlyph supports CJK IME for Japanese, Chinese, and Korean input.
macOS overlay API (v1.8+):
$if darwin {
ns_window := C.sapp_macos_get_window()
ime_overlay := vglyph.ime_overlay_create_auto(ns_window)
vglyph.ime_overlay_register_callbacks(ime_overlay,
on_marked_text, on_insert_text,
on_do_command, on_get_rect, on_clause, user_data)
vglyph.ime_overlay_set_focused_field(ime_overlay, 'field_id')
}
Cross-platform global callbacks (v1.3+):
For simpler single-field apps, global callbacks work on all platforms.
See EDITING.md for API details and IME-APPENDIX.md for CJK IME architecture.
Known issue: Korean first-keypress macOS bug (QTBUG-136128, FB17460926).
You must have Pango and FreeType installed.
brew install pango freetype pkg-config
sudo apt-get install libpango1.0-dev libfreetype6-dev pkg-config
# For comprehensive emoji and multilingual support
sudo apt-get install fonts-noto fonts-noto-color-emoji
# vcpkg
vcpkg install pango freetype
# Or MSYS2
pacman -S mingw-w64-x86_64-pango mingw-w64-x86_64-freetype
Use the TextSystem for the easiest integration. It handles initialization,
caching, and rendering.
import vglyph
import gg
struct App {
mut:
gg &gg.Context = unsafe { nil }
ts &vglyph.TextSystem = unsafe { nil }
}
fn main() {
mut app := &App{}
app.gg = gg.new_context(
bg_color: gg.white
width: 800
height: 600
window_title: 'VGlyph Demo'
init_fn: init
frame_fn: frame
user_data: app
)
app.gg.run()
}
fn init(mut app App) {
// 1. Initialize TextSystem
app.ts = vglyph.new_text_system(mut app.gg) or { panic(err) }
}
fn frame(mut app App) {
app.gg.begin()
// 2. Draw Text
// Coordinates are (x, y)
app.ts.draw_text(100, 100, 'Hello VGlyph!', vglyph.TextConfig{
style: vglyph.TextStyle{
font_name: 'Sans Bold 30'
color: gg.black
}
}) or { println(err) }
app.gg.end()
// 3. Commit Texture Uploads (Important!)
app.ts.commit()
}
The examples/ directory contains several demonstrations:
demo.v - Multilingual text, wrapping, and rich text markupemoji_demo.v - Emoji and color bitmap renderingeditor_demo.v - Interactive text editing with CJK IME supporttypography_demo.v - OpenType features and custom tab stopsvariable_font_demo.v - Variable font animation (weight/width axes)list_demo.v - Unordered and ordered lists with hanging indentsrotate_text.v - Rotated text with animated angletransform_text.v - Matrix transform text (rotate + translate + skew)stress_demo.v - Performance testing with thousands of glyphsRun any example with:
v run examples/demo.v
width := app.ts.text_width('Hello', cfg)!
height := app.ts.text_height('Hello', cfg)!
font_h := app.ts.font_height(cfg)
// Check which font Pango actually resolved
actual_font := app.ts.resolve_font_name('Sans Bold 30')
println('Using font: ${actual_font}')
// Get layout for hit testing or custom rendering
layout := app.ts.layout_text('Click me', cfg)!
char_idx := layout.hit_test(mouse_x, mouse_y)
rects := layout.get_selection_rects(0, 5)
layout := app.ts.layout_text('Affine Text', cfg)!
t := vglyph.AffineTransform{
xx: 1.0
xy: 0.25 // skew X
yx: 0.0
yy: 1.0
x0: 20.0 // local translation
y0: 0.0
}
app.ts.draw_layout_transformed(layout, 200, 180, t)
// Import vglyph
rt := vglyph.RichText{
runs: [
vglyph.StyleRun{ text: 'Hello ' },
vglyph.StyleRun{
text: 'World',
style: vglyph.TextStyle{
color: gg.red,
underline: true
}
}
]
}
layout := app.ts.layout_rich_text(rt, cfg)!
app.ts.draw_layout(layout, x, y)
Ensure you're calling commit() at the end of each frame. Without it, new
glyphs won't upload to the GPU.
Install comprehensive fallback fonts:
sudo apt-get install fonts-noto fonts-noto-color-emojiUse the font's family name, not the filename. Check with:
println(app.ts.resolve_font_name('YourFont'))
TextSystem automatically caches layouts. If text changes every frame (e.g.,
FPS counter), consider using a monospace font and fixed-width formatting to
minimize layout variations.
MIT
V
84.1%
C
9.2%
Objective-C
6.7%