Module allsorts::outline[][src]

Expand description

Access glyphs outlines. Requires the outline cargo feature (enabled by default).

This module is used to access the outlines of glyphs as a series of foundational drawing instruction callbacks on implementors of the OutlineSink trait. Outlines from glyf and CFF tables can be accessed.

Example

This is a fairly complete example of mapping some glyphs and then visiting their outlines with support for TrueType and CFF fonts. It accumulates the drawing operations into a String. In a real application you’d probably make calls to a graphics library instead.

use std::fmt::Write;

use allsorts::binary::read::ReadScope;
use allsorts::cff::CFF;
use allsorts::font::{GlyphTableFlags, MatchingPresentation};
use allsorts::font_data::FontData;
use allsorts::gsub::RawGlyph;
use allsorts::outline::{OutlineBuilder, OutlineSink};
use allsorts::pathfinder_geometry::line_segment::LineSegment2F;
use allsorts::pathfinder_geometry::vector::Vector2F;
use allsorts::tables::glyf::GlyfTable;
use allsorts::tables::loca::LocaTable;
use allsorts::tables::{FontTableProvider, SfntVersion};
use allsorts::{tag, Font};

struct DebugVisitor {
    outlines: String,
}

impl OutlineSink for DebugVisitor {
    fn move_to(&mut self, to: Vector2F) {
        writeln!(&mut self.outlines, "move_to({}, {})", to.x(), to.y()).unwrap();
    }

    fn line_to(&mut self, to: Vector2F) {
        writeln!(&mut self.outlines, "line_to({}, {})", to.x(), to.y()).unwrap();
    }

    fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
        writeln!(
            &mut self.outlines,
            "quad_to({}, {}, {}, {})",
            control.x(),
            control.y(),
            to.x(),
            to.y()
        )
        .unwrap();
    }

    fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
        writeln!(
            &mut self.outlines,
            "curve_to({}, {}, {}, {}, {}, {})",
            control.from_x(),
            control.from_y(),
            control.to_x(),
            control.to_y(),
            to.x(),
            to.y()
        )
        .unwrap();
    }

    fn close(&mut self) {
        writeln!(&mut self.outlines, "close()").unwrap();
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let script = tag::LATN;
    let buffer = std::fs::read("tests/fonts/opentype/Klei.otf")?;
    let scope = ReadScope::new(&buffer);
    let font_file = scope.read::<FontData<'_>>()?;
    let provider = font_file.table_provider(0)?;
    let mut font = Font::new(provider)?.ok_or("cmap missing")?;
    let mut sink = DebugVisitor {
        outlines: String::new(),
    };

    // Map text to glyphs
    let glyphs = font.map_glyphs("+", script, MatchingPresentation::NotRequired);

    // Visit the outlines of each glyph. Read tables depending on the type of font
    if font.glyph_table_flags.contains(GlyphTableFlags::CFF)
        && font.font_table_provider.sfnt_version() == tag::OTTO
    {
        let cff_data = font.font_table_provider.read_table_data(tag::CFF)?;
        let mut cff = ReadScope::new(&cff_data).read::<CFF<'_>>()?;
        sink.glyphs_to_path(&mut cff, &glyphs)?;
    } else if font.glyph_table_flags.contains(GlyphTableFlags::GLYF) {
        let loca_data = font.font_table_provider.read_table_data(tag::LOCA)?;
        let loca = ReadScope::new(&loca_data).read_dep::<LocaTable<'_>>((
            usize::from(font.maxp_table.num_glyphs),
            font.head_table()?
                .ok_or("missing head table")?
                .index_to_loc_format,
        ))?;
        let glyf_data = font.font_table_provider.read_table_data(tag::GLYF)?;
        let mut glyf = ReadScope::new(&glyf_data).read_dep::<GlyfTable<'_>>(&loca)?;
        sink.glyphs_to_path(&mut glyf, &glyphs)?;
    } else {
        return Err("no glyf or CFF table".into());
    }

    let expected = "move_to(225, 152)
line_to(225, 269)
curve_to(225, 274, 228, 276, 232, 276)
line_to(341, 276)
curve_to(346, 276, 347, 285, 347, 295)
curve_to(347, 307, 345, 320, 341, 320)
line_to(232, 320)
curve_to(226, 320, 226, 325, 226, 328)
line_to(226, 432)
curve_to(220, 435, 214, 437, 206, 437)
curve_to(198, 437, 190, 435, 181, 432)
line_to(181, 329)
curve_to(181, 326, 180, 320, 172, 320)
line_to(68, 320)
curve_to(62, 320, 59, 311, 59, 300)
curve_to(59, 289, 62, 278, 68, 276)
line_to(174, 276)
curve_to(179, 276, 181, 271, 181, 267)
line_to(181, 152)
curve_to(181, 147, 193, 144, 204, 144)
curve_to(215, 144, 225, 147, 225, 152)
close()
";
    assert_eq!(sink.outlines, expected);
    Ok(())
}

impl DebugVisitor {
    pub fn glyphs_to_path<T>(
        &mut self,
        builder: &mut T,
        glyphs: &[RawGlyph<()>],
    ) -> Result<(), Box<dyn std::error::Error>>
    where
        T: OutlineBuilder,
        <T as OutlineBuilder>::Error: 'static,
    {
        for glyph in glyphs {
            builder.visit(glyph.glyph_index, self)?;
        }

        Ok(())
    }
}

Traits

Trait for visiting a glyph outline and delivering drawing commands to an OutlineSink.

A trait for visiting a glyph outline