#harfbuzz #shaping #textlayout

sys harfbuzz_rs_now

A high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust

8 releases (stable)

new 2.2.6 Dec 11, 2024
2.2.4 Dec 2, 2024
2.2.3 Nov 27, 2024

#466 in Text processing

Download history 316/week @ 2024-11-11 19/week @ 2024-11-18 92/week @ 2024-11-25 151/week @ 2024-12-02 820/week @ 2024-12-09

1,095 downloads per month
Used in cn-font-split

MIT license

2MB
46K SLoC

C++ 34K SLoC // 0.3% comments Rust 8K SLoC // 0.0% comments Python 3.5K SLoC // 0.1% comments Templ 40 SLoC // 0.6% comments C 32 SLoC // 0.4% comments

harfbuzz_rs_now

Crates.io Documentation Build status

harfbuzz_rs_now is a high-level interface to HarfBuzz, exposing its most important functionality in a safe manner using Rust. And we provide more powerful methods to you.

  • normal usage
  • subset font
  • svg string output
  • wasi32 support build!

What is HarfBuzz?

HarfBuzz is a library for performing complex text layout. It does not perform any drawing. This is quite a low-level operation. If you want to simply draw some text on the screen you should maybe choose another more high-level library. However if you want to build a library for drawing text on some canvas or need a lot of control on advanced text layout then this is the right library to use.

Getting Started

To shape a simple string of text you just create a Font from a font file, fill a Buffer with some text and call the shape function.

use harfbuzz_rs_now::*;

fn main() {
    let path = "./src/SourceSansVariable-Roman.ttf";
    let index = 0; //< face index in the font file
    let mut face = Face::from_file(path, index).unwrap();

    // create a font subset
    let font_subset = subset::Subset::new();

    let chars: [u32; 3] = [32, 33, 34];
    font_subset.add_chars(&chars);

    // Preserve all OpenType tables (like glyf, cmap, etc.) for full font functionality
    font_subset.clear_drop_table();
    // Maintain all OpenType layout features (like ligatures, kerning) and script support
    font_subset.adjust_layout();

    let new_font_face = font_subset.run_subset(&face);
    let shared_data = new_font_face.face_data();
    let binary_data_of_subset = shared_data.get_data();
    std::fs::write("subset.ttf", binary_data_of_subset).expect("Failed to write font subset");

    let mut font = Font::new(face);

    let buffer = UnicodeBuffer::new().add_str("Hello World!");

    let output = shape(&font, buffer, &[]);

    // The results of the shaping operation are stored in the `output` buffer.

    let positions = output.get_glyph_positions();
    let infos = output.get_glyph_infos();

    // iterate over the shaped glyphs
    for (position, info) in positions.iter().zip(infos) {
        let gid = info.codepoint;
        let cluster = info.cluster;
        let x_advance = position.x_advance;
        let x_offset = position.x_offset;
        let y_offset = position.y_offset;

        // Here you would usually draw the glyphs.
        println!(
            "gid{:?}={:?}@{:?},{:?}+{:?}",
            gid, cluster, x_advance, x_offset, y_offset
        );
    }

    // render a simple string to svg!
    let svg_string = font.render_svg_text("Hello World\nI can see you", &[]);
    println!("{}", svg_string);
}

This should print out something similar to the following:

gid41=0@741,0+0
gid70=1@421,0+0
gid77=2@258,0+0
gid77=3@253,0+0
gid80=4@510,0+0
gid1=5@227,0+0
gid56=6@874,0+0
gid80=7@498,0+0
gid83=8@367,0+0
gid77=9@253,0+0
gid69=10@528,0+0
gid2=11@276,0+0

The values of x_advance, x_offset, y_advance and y_offset are all given in so-called font units by default. By calling face.upem() you get the number of font units per EM for a specific face. This upem value can be used to scale the advances and offsets to a given font-size. For example, if you want to display a font at 16 point (pt) size, that means that 1 EM = 16 pt. In this example, to convert a value, say x_advance, from font-units to points, we compute ((x_advance * font_size) as f64) / (upem as f64), where font_size = 16 is a variable specifying the font size in points.

Note that harfbuzz internally supports scaling fonts itself as well (using font.set_scale(...), etc.) but in my opinion it is easier to scale the results oneself as described in the paragraph above.

Supported HarfBuzz versions

This crate is tested to work with harfbuzz versions 2.0 and higher. Older versions may work as well. I recommend statically linking the harfbuzz library provided by the harfbuzz-sys crate which is always up-to-date.

Wasm32-wasi Support

First, install the wasi-sdk, then set the WASI_SYSROOT environment variable to the share/wasi-sysroot path of your wasi-sdk. Finally, run cargo build --target wasm32-wasi.

apt install wget
WASI_OS=linux
WASI_ARCH=x86_64
WASI_VERSION=24
WASI_VERSION_FULL=${WASI_VERSION}.0
wget https://github.moeyy.xyz/https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz
if [ ! -d "/opt/wasi-sdk" ]; then
  mkdir /opt/wasi-sdk
fi
tar xvf wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz -C /opt/wasi-sdk
rm -rf wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz
export WASI_SYSROOT="/opt/wasi-sdk/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}/share/wasi-sysroot"
echo -e "\nexport WASI_SYSROOT=\"${WASI_SYSROOT}\"\n" >> ~/.bashrc

Optional Features

If you want to use rusttype as font functions enable the rusttype feature.

Dependencies