fonts

    Experimental package to manipulate open source font data.

    html5 canvas
    pdf
    svg
    open source
    fonts
    Download zip
    Author
    Version
    0.19.16
    License
    Apache-2.0
    Last updated
    7 days ago
    Downloads
    11K

    Dependencies

    #gmlewis/moonbit-fonts

    check

    This is an experimental package to manipulate open source fonts with MoonBit.

    All fonts are open source and their licenses can all be found in their corresponding repos which are organized by the first letter of the name of the font:

    mbt-fonts-* packages contain hardcoded font data using Map[String, @fonts.Glyph].

    To add a font dependency, use the MoonBit package name (which follows the gmlewis/fonts-<letter>/<fontname> pattern). For example, to add the baloo font, run:
    moon add gmlewis/fonts-b/baloo

    The corresponding GitHub repositories are:

    • font: https://github.com/gmlewis/mbt-fonts-a/tree/master/aaarghnormal
    • license: https://github.com/gmlewis/go-fonts-a/tree/master/fonts/aaarghnormal
    • ...
    • font: https://github.com/gmlewis/mbt-fonts-b/tree/master/baloo
    • license: https://github.com/gmlewis/go-fonts-b/tree/master/fonts/baloo
    • ...
    • font: https://github.com/gmlewis/mbt-fonts-z/tree/master/znikomitno24
    • license: https://github.com/gmlewis/go-fonts-z/tree/master/fonts/znikomitno24

    #Dynamic Font Loading

    For projects that need to load fonts on-demand by name (e.g., from a configuration file) without compiling every font into the binary, you can use the Font::load_font API.

    This requires the all-fonts/ directory to be available on your filesystem. This directory contains compressed JSON files for each font (e.g., baloo.json.gz).

    #Usage

    // Load a font by name
    let font = @fonts.Font::load_font("baloo")

    #Search Logic

    load_font searches for the font in the following order:
    1. Relative to the current working directory: all-fonts/{font_name}.json.gz.
    2. Relative to the MOONBIT_FONTS_DIR environment variable: ${MOONBIT_FONTS_DIR}/all-fonts/{font_name}.json.gz.

    #Setting up MOONBIT_FONTS_DIR

    If you have a central location for your font data, set the environment variable:

    export MOONBIT_FONTS_DIR=/path/to/your/gmlewis/fonts/repo

    The directory should contain an all-fonts/ subdirectory with the .json.gz files.

    #Quick Start

    See the examples/quick-start directory for a valid example of how to use this package.

    #Functional Per-Grapheme Text Styling

    The @draw.text_with API allows custom styling per grapheme without relying on internal Path / CompoundPath structure.

    #Callback Model

    • @draw.text_with_style_fn((ctx) => action) creates a callback.
    • ctx is GlyphStyleContext with:
      • grapheme_index
      • grapheme
      • line_index
      • grapheme_index_in_line
    • Return a GlyphStyleAction via helpers:
      • @draw.glyph_style_use_default()
      • @draw.glyph_style_override(stroke=?, fill=?)
      • @draw.glyph_style_skip()

    #Example (marker-based styling)

    This example skips *, colors the next visible glyph red, and keeps all others black:

    let y_label = @draw.text_with(
    font,
    text,
    align=Right,
    style_fn=@draw.text_with_style_fn(fn(ctx) {
    if ctx.grapheme == "*" {
    @draw.glyph_style_skip()
    } else if ctx.grapheme_index == 1 {
    @draw.glyph_style_override(fill=red_fill)
    } else {
    @draw.glyph_style_override(fill=black_fill)
    }
    }),
    )
    .transform(scale=vec2(0.2, 0.2), position=y_tick_pos + vec2(-0.02, 0.05))

    graph.push(y_label)

    #Tuple Compatibility API

    If you prefer the original tuple callback style, use @draw.text_with_tuple with @draw.text_with_tuple_style_fn((index, grapheme) -> (stroke?, fill?)).

    #Examples

    #checkerboard

    checkerboard is a simple example to create a draw.Graphic using the @draw API.

    #canvas-checkerboard

    canvas-checkerboard renders the checkerboard Graphic to an HTML5 canvas. Type ./run-canvas-checkerboard.sh in a terminal then open your browser to http://localhost:8080/examples/canvas-checkerboard to view it.

    #svg-checkerboard

    svg-checkerboard "renders" the checkerboard Graphic to an SVG file. Type moon run examples/svg-checkerboard > examples/svg-checkerboard/checkerboard.svg in a terminal then open this file in your browser to view it. For example, google-chrome examples/svg-checkerboard/checkerboard.svg.

    #Scripts

    A few utility scripts are available in the scripts/ directory to help with common tasks.

    #render-to-svg.py

    Quickly render text to an SVG file using any available font. This script handles creating a temporary MoonBit project, importing the necessary font packages, and running the code to generate the SVG. It supports multi-line text and custom alignment.

    Examples:

    # Render "Hello World" using the default font (aaarghnormal) ./scripts/render-to-svg.py "Hello World" -o hello.svg # Render multi-line text with horizontal centering ./scripts/render-to-svg.py "Line 1\nLine 2 Centered" -a center -o centered.svg # Render text using a specific font (e.g. baloo) ./scripts/render-to-svg.py "Custom Font" -f baloo -o custom.svg # List all available font families ./scripts/render-to-svg.py --list-fonts # Use Markdown-style markers for bold/italic if the font family supports them ./scripts/render-to-svg.py "**Bold Text** and *Italic Text*" -f aileron -o styled.svg

    #render-to-json.py

    Similar to render-to-svg.py, but outputs a serialized JSON representation of the draw.Graphic object instead of an SVG. This is useful for passing graphic data to other tools like ray tracers. It defaults to y-up coordinates.

    Examples:

    # Render text to JSON (defaults to y-up) ./scripts/render-to-json.py "Data Graphic" -f abeezee -o graphic.json # Render multi-line text with right-alignment and y-down coordinates ./scripts/render-to-json.py "Line 1\nLine 2" -a right --y-down -o down.json

    #sample-all-fonts.py

    Generates one or more SVG files showing a sample of text rendered in every available font. This is useful for visual font selection.

    Examples:

    # Generate sample SVGs for all fonts (in batches of 221) ./scripts/sample-all-fonts.py -o all-fonts.svg "The quick brown fox" # Use a specific label font ./scripts/sample-all-fonts.py -o samples.svg --label-font aileron_bold

    #Status

    The code has been updated to support compiler:

    $ moon version --all moon 0.1.20260904 (94521db 2026-09-04) ~/.moon/bin/moon moonc v0.10.12+1634b282e (2026-09-07) ~/.moon/bin/moonc moonrun 0.1.20260904 (94521db 2026-09-04) ~/.moon/bin/moonrun


    Enjoy!


    #License

    Please note that all fonts have their own licenses which are included in their respective original directories (see above).

    Copyright 2019-2024 Glenn M. Lewis. All Rights Reserved.

    Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.


    FontError

    pub(all) suberror FontError {
    FontError(String)
    } derive(Eq)

    FontError represents an error that occurred during font processing.
    impl Show for FontError

    AbsoluteCmd

    pub(all) enum AbsoluteCmd {
    M
    L
    C
    Q
    Z
    } derive(Eq,
    Debug
    )

    AbsoluteCmd represents a supported absolute SVG command.
    impl Show for AbsoluteCmd

    Cmd

    Cmd represents an SVG command along with its parameters.
    impl Show for Cmd

    Font

    pub(all) struct Font {
    id : String
    horiz_adv_x : Double
    units_per_em : Double
    ascent : Double
    descent : Double
    glyphs : Map[String, Glyph]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Font represents an entire font.
    impl Show for Font

    Font::gen_path

    fn Font::gen_path(self : Font, text : String, alignment? :
    Alignment
    , y_up? : Bool) -> Glyph raise FontError

    gen_path "renders" the provided text string into an SVG path using the provided font information. It performs the necessary path translations in order to combine the individual glyphs into a "super glyph" that can be rendered as a whole.

    Note that gen_path operates on the string as a left-justified whole and aligns the entire path relative to alignment accordingly.

    If you want multiple lines centered horizontally, use gen_paths instead.

    Note that the SVG standard states that positive-Y is "down" with origin coordinates in the upper-left, however, all SVG fonts appear to have their origin in the lower left with positive-Y moving "up". This package attempts to convert the SVG paths such that the font is equally readable in both the y_up=false SVG canvas environment or in a 3D y_up=true rendering environment.

    Font::gen_paths

    fn Font::gen_paths(self : Font, lines : Array[String], alignment? :
    Alignment
    , y_up? : Bool) -> Array[Glyph] raise FontError

    gen_paths handles multiple strings independently so that they can be aligned and distributed more flexibly.

    GerberLP

    pub(all) enum GerberLP {
    Dark
    Clear
    } derive(Eq,
    Debug
    )

    GerberLP represents whether a subpath is Dark or Clear.
    impl Show for GerberLP

    Glyph

    pub(all) struct Glyph {
    char : String
    horiz_adv_x : Double
    gerber_lp : String
    d : String
    xmin : Double
    ymin : Double
    xmax : Double
    ymax : Double
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Glyph represents a single glyph within each Font and also represents multiple glyphs combined together (for example after using Font.gen_path and switching back and forth between Glyphs which are optimized for storage, and Paths which are optimized for processing).
    impl Show for Glyph

    ParamPair

    pub(all) struct ParamPair {
    x : Double
    y : Double
    } derive(Eq,
    Debug
    )

    ParamPair represents an X,Y absolute coordinate pair of parameters.
    impl Show for ParamPair

    ParamPair::to_string

    fn ParamPair::to_string(self : ParamPair) -> String

    to_string returns a string representation of a ParamPair.

    Params

    Params represents the parameters to an SVG command.
    impl Show for Params

    Params::length

    fn Params::length(self : Params) -> Int

    PathCmd

    pub(all) struct PathCmd {
    cmd : AbsoluteCmd
    gerber_lp : GerberLP
    params : Array[ParamPair]
    } derive(Eq,
    Debug
    )

    PathCmd represents an individual absolute SVG command.
    impl Show for PathCmd

    PathCmd::bbox

    bbox returns the minimum bounding box of a PathCmd.

    PathCmd::clone

    fn PathCmd::clone(self : PathCmd) -> PathCmd

    clone makes a deep copy of a PathCmd.

    PathCmd::from_svg_cmd

    fn PathCmd::from_svg_cmd(svg_cmd : Cmd, gerber_lp : GerberLP) -> PathCmd raise FontError

    from_svg_cmd creates a PathCmd from a raw SVG command and a GerberLP.

    PathCmd::to_svg_cmd

    fn PathCmd::to_svg_cmd(self : PathCmd) -> (Cmd, String)

    to_svg_cmd converts a PathCmd back to a raw SVG command and its GerberLP code.

    PathCmdFn

    pub(all) struct PathCmdFn((Int, PathCmd) -> PathCmd)

    PathCmdFn represents a function that processes or transforms individual commands. The first argument is the index within the command.

    SVGPath

    pub(all) struct SVGPath {
    char : String
    cmds : Array[PathCmd]
    xmin : Double
    ymin : Double
    xmax : Double
    ymax : Double
    } derive(Eq)

    An SVGPath is identical to a Glyph but has been optimized internally for further manipulation, whereas a Glyph is optimized for compact storage of font data within all the font packages. An SVGPath can be converted to a Glyph and vice versa.
    impl Show for SVGPath

    SVGPath::clone

    fn SVGPath::clone(self : SVGPath) -> SVGPath

    clone makes a deep copy of an SVGPath.

    SVGPath::from_glyph

    fn SVGPath::from_glyph(g : Glyph, path_cmd_fn? : PathCmdFn) -> SVGPath raise FontError

    from_glyph returns an SVGPath from a Glyph, optionally processing every Cmd with a processing function. Note that apart from path_cmd_fn, from_glyph makes no attempt to process the individual glyphs and simply transforms the representation.

    SVGPath::to_glyph

    fn SVGPath::to_glyph(self : SVGPath, path_cmd_fn? : PathCmdFn) -> Glyph

    to_glyph returns a "super" Glyph from an SVGPath, optionally processing every PathCmd with a processing function. Note that apart from path_cmd_fn, to_glyph makes no attempt to process the individual glyphs and simply transforms the representation.

    glyph_bbox

    fn glyph_bbox(d : String) ->
    BoundingBox
    raise FontError

    glyph_bbox returns the conservative bounding box of a Glyph path. It only supports the absolute SVG commands: M, L, C, Q, Z.

    Note that this function does NOT fully analyze the Cubic or Quadratic Bézier curves to determine their exact bounding boxes, but instead takes a naive and conservative approach by encapsulating the bounds of all control points in addition to curve anchor points which may result in returning a much larger bounding box than is actually needed to fully contain the glyph.

    translate_path

    fn translate_path(d : String, x : Double, y : Double, invert_y? : Bool) -> String raise FontError

    translate_path translates (moves) an SVG path by the provided offsets. If invert_y is true, all y values are scaled by -1.