Function allsorts_no_std::gsub::apply[][src]

pub fn apply(
    dotted_circle_index: u16,
    gsub_cache: &LayoutCache<GSUB>,
    opt_gdef_table: Option<&GDEFTable>,
    script_tag: u32,
    opt_lang_tag: Option<u32>,
    features: &Features,
    num_glyphs: u16,
    glyphs: &mut Vec<RawGlyph<()>>
) -> Result<(), ShapingError>

Perform glyph substitution according to the supplied features, script and language.

dotted_circle_index is the glyph index of U+25CC DOTTED CIRCLE: ◌. This is inserted when shaping some complex scripts where the input text contains incomplete syllables. If you have an instance of FontDataImpl the glyph index can be retrieved via the lookup_glyph_index method.

Example

The following shows a complete example of loading a font, mapping text to glyphs, and applying glyph substitution.

use core::convert::TryFrom;
use core::error::Error;
use alloc::rc::Rc;

use allsorts::binary::read::ReadScope;
use allsorts::error::ParseError;
use allsorts::font::{MatchingPresentation};
use allsorts::font_data::FontData;
use allsorts::gsub::{Features, GlyphOrigin, GsubFeatureMask, RawGlyph};
use allsorts::tinyvec::tiny_vec;
use allsorts::unicode::VariationSelector;
use allsorts::DOTTED_CIRCLE;
use allsorts::{gsub, tag, Font};

fn shape(text: &str) -> Result<Vec<RawGlyph<()>>, Box<dyn Error>> {
    let script = tag::from_string("LATN")?;
    let lang = tag::from_string("DFLT")?;
    let buffer = core::fs::read("tests/fonts/opentype/Klei.otf")
        .expect("unable to read Klei.otf");
    let scope = ReadScope::new(&buffer);
    let font_file = scope.read::<FontData<'_>>()?;
    // Use a different index to access other fonts in a font collection (E.g. TTC)
    let provider = font_file.table_provider(0)?;
    let mut font = match Font::new(provider)? {
        Some(font) => font,
        None => {
            return Err(Box::from(ParseError::MissingValue));
        }
    };

    let opt_gsub_cache = font.gsub_cache()?;
    let opt_gpos_cache = font.gpos_cache()?;
    let opt_gdef_table = font.gdef_table()?;
    let opt_gdef_table = opt_gdef_table.as_ref().map(Rc::as_ref);

    // Map glyphs
    //
    // We look ahead in the char stream for variation selectors. If one is found it is used for
    // mapping the current glyph. When a variation selector is reached in the stream it is
    // skipped as it was handled as part of the preceding character.
    let mut chars_iter = text.chars().peekable();
    let mut glyphs = Vec::new();
    while let Some(ch) = chars_iter.next() {
        match VariationSelector::try_from(ch) {
            Ok(_) => {} // filter out variation selectors
            Err(()) => {
                let vs = chars_iter
                    .peek()
                    .and_then(|&next| VariationSelector::try_from(next).ok());
                let (glyph_index, used_variation) = font.lookup_glyph_index(
                    ch,
                    MatchingPresentation::NotRequired,
                    vs,
                );
                let glyph = RawGlyph {
                    unicodes: tiny_vec![[char; 1] => ch],
                    glyph_index: glyph_index,
                    liga_component_pos: 0,
                    glyph_origin: GlyphOrigin::Char(ch),
                    small_caps: false,
                    multi_subst_dup: false,
                    is_vert_alt: false,
                    fake_bold: false,
                    fake_italic: false,
                    extra_data: (),
                    variation: Some(used_variation),
                };
                glyphs.push(glyph);
            }
        }
    }

    let (dotted_circle_index, _) = font.lookup_glyph_index(
        DOTTED_CIRCLE,
        MatchingPresentation::NotRequired,
        None,
    );

    // Apply gsub if table is present
    let num_glyphs = font.num_glyphs();
    if let Some(gsub_cache) = opt_gsub_cache {
        gsub::apply(
            dotted_circle_index,
            &gsub_cache,
            opt_gdef_table,
            script,
            Some(lang),
            &Features::Mask(GsubFeatureMask::default()),
            num_glyphs,
            &mut glyphs,
        )?;
    }

    // This is where you would apply `gpos` if the table is present.

    Ok(glyphs)
}

match shape("This is the first example.") {
    Ok(glyphs) => {
        assert!(!glyphs.is_empty());
    }
    Err(err) => panic!("Unable to shape text: {}", err),
}