canary-rs/src/text.rs

393 lines
12 KiB
Rust
Raw Normal View History

2022-07-16 17:44:22 +00:00
use super::{DrawCommand, MeshIndex, MeshVertex};
use allsorts::binary::read::ReadScope;
use allsorts::cff::CFF;
use allsorts::font::{GlyphTableFlags, MatchingPresentation};
2022-07-18 21:51:18 +00:00
use allsorts::font_data::{DynamicFontTableProvider, FontData as AllsortsFontData};
2022-07-16 17:44:22 +00:00
use allsorts::glyph_position::{GlyphLayout, TextDirection};
use allsorts::outline::OutlineBuilder;
use allsorts::pathfinder_geometry::{line_segment::LineSegment2F, vector::Vector2F};
use allsorts::tables::{glyf::GlyfTable, loca::LocaTable};
use allsorts::tables::{FontTableProvider, SfntVersion};
use allsorts::{tag, Font as AllsortsFont};
2022-07-17 01:26:02 +00:00
use canary_types::GlyphPosition;
2022-07-16 20:39:11 +00:00
use lyon::path::Path;
use ouroboros::self_referencing;
2022-07-18 21:51:18 +00:00
use parking_lot::{Mutex, RwLock};
2022-07-16 17:44:22 +00:00
const TEXT_SCALE: f32 = 0.075;
2022-07-16 17:44:22 +00:00
pub struct GlyphCache {
units_per_em: f32,
glyphs: RwLock<Vec<CachedGlyph>>,
2022-07-16 17:44:22 +00:00
}
impl GlyphCache {
pub fn new(font: &AllsortsFont<DynamicFontTableProvider>) -> Self {
2022-07-16 20:39:11 +00:00
let glyph_num = font.num_glyphs();
let mut glyphs = Vec::with_capacity(glyph_num as usize);
let units_per_em = font.head_table().unwrap().unwrap().units_per_em as f32;
2022-07-16 20:39:11 +00:00
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).unwrap();
let mut cff = ReadScope::new(&cff_data).read::<CFF<'_>>().unwrap();
for index in 0..glyph_num {
glyphs.push(CachedGlyph::new(units_per_em, &mut cff, index));
2022-07-16 20:39:11 +00:00
}
} else if font.glyph_table_flags.contains(GlyphTableFlags::GLYF) {
let loca_data = font.font_table_provider.read_table_data(tag::LOCA).unwrap();
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((
usize::from(font.maxp_table.num_glyphs),
font.head_table().unwrap().unwrap().index_to_loc_format,
))
.unwrap();
let glyf_data = font.font_table_provider.read_table_data(tag::GLYF).unwrap();
let mut glyf = ReadScope::new(&glyf_data)
.read_dep::<GlyfTable<'_>>(&loca)
.unwrap();
for index in 0..glyph_num {
glyphs.push(CachedGlyph::new(units_per_em, &mut glyf, index));
2022-07-16 20:39:11 +00:00
}
} else {
panic!("no glyf or CFF table");
2022-07-16 20:39:11 +00:00
}
Self {
units_per_em,
glyphs: RwLock::new(glyphs),
2022-07-16 17:44:22 +00:00
}
}
2022-07-16 20:39:11 +00:00
}
2022-07-16 17:44:22 +00:00
2022-07-16 20:39:11 +00:00
pub struct CachedGlyph {
path: Path,
mesh: Option<GlyphMesh>,
}
impl CachedGlyph {
pub fn new(units_per_em: f32, builder: &mut impl OutlineBuilder, index: u16) -> Self {
let mut sink = OutlineSink::new(units_per_em);
2022-07-16 17:44:22 +00:00
builder.visit(index, &mut sink).unwrap();
let path = sink.builder.build();
2022-07-16 20:39:11 +00:00
Self { path, mesh: None }
}
pub fn get_mesh(&mut self) -> &GlyphMesh {
self.mesh.get_or_insert_with(|| {
use lyon::tessellation::*;
let mut geometry: VertexBuffers<MeshVertex, MeshIndex> = VertexBuffers::new();
let fill_options = FillOptions::default().with_tolerance(0.0005);
2022-07-16 20:39:11 +00:00
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_path(
&self.path.clone(),
&fill_options,
2022-07-16 20:39:11 +00:00
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
let position = vertex.position() * TEXT_SCALE;
let position = canary_types::Vec2 {
x: position.x,
y: position.y,
};
MeshVertex {
position,
color: canary_types::Color::WHITE,
}
}),
)
.unwrap();
2022-07-16 17:44:22 +00:00
2022-07-16 20:39:11 +00:00
GlyphMesh {
2022-07-16 17:44:22 +00:00
vertices: geometry.vertices,
indices: geometry.indices,
2022-07-16 20:39:11 +00:00
}
})
2022-07-16 17:44:22 +00:00
}
}
2022-07-16 20:39:11 +00:00
pub struct GlyphMesh {
2022-07-16 17:44:22 +00:00
vertices: Vec<MeshVertex>,
indices: Vec<MeshIndex>,
}
pub struct Font {
2022-07-18 21:51:18 +00:00
data: Mutex<FontData>,
glyph_cache: GlyphCache,
}
impl Font {
pub fn load(
face_index: u16,
script: u32,
direction: TextDirection,
vertical: bool,
file_buffer: Vec<u8>,
) -> Self {
let mut data = FontData::load(face_index, script, direction, vertical, file_buffer);
let glyph_cache = data.with_inner_mut(|font| GlyphCache::new(font));
2022-07-18 21:51:18 +00:00
let data = Mutex::new(data);
Self { data, glyph_cache }
}
pub fn shape(&self, text: &str) -> Vec<GlyphPosition> {
self.data.lock().shape(text)
}
pub fn draw(&self, mesh: &mut DummyText, positions: &[GlyphPosition], yoff: f32) {
// TODO defer tessellation of missed glyph cache entries
let mut glyphs = self.glyph_cache.glyphs.write();
let units_per_em = self.glyph_cache.units_per_em;
let mut xcur = 0;
let mut ycur = 0;
for position in positions.iter() {
let glyph = glyphs.get_mut(position.index as usize).unwrap().get_mesh();
let xpos = xcur + position.xoff;
let ypos = ycur + position.yoff;
xcur += position.hori_advance;
ycur += position.vert_advance;
let voff = mesh.vertices.len() as MeshIndex;
let scale = TEXT_SCALE / units_per_em;
let xpos = xpos as f32 * scale;
let ypos = ypos as f32 * scale;
for v in glyph.vertices.iter() {
let x = v.position.x + xpos - 0.9;
let y = v.position.y + ypos + yoff;
mesh.vertices.push(MeshVertex {
position: canary_types::Vec2 { x, y },
color: v.color,
});
}
for i in glyph.indices.iter() {
mesh.indices.push(i + voff);
}
}
}
2022-07-18 21:51:18 +00:00
}
#[self_referencing]
pub struct FontData {
file_buffer: Vec<u8>,
2022-07-17 01:26:02 +00:00
face_index: u16,
script: u32,
direction: TextDirection,
vertical: bool,
#[borrows(file_buffer)]
#[covariant]
read_scope: ReadScope<'this>,
#[borrows(read_scope)]
#[not_covariant]
2022-07-18 21:51:18 +00:00
font_data: AllsortsFontData<'this>,
#[borrows(font_data)]
#[covariant]
inner: AllsortsFont<DynamicFontTableProvider<'this>>,
}
2022-07-18 21:51:18 +00:00
impl FontData {
2022-07-17 01:26:02 +00:00
pub fn load(
face_index: u16,
script: u32,
direction: TextDirection,
vertical: bool,
file_buffer: Vec<u8>,
) -> Self {
2022-07-18 21:51:18 +00:00
FontDataBuilder {
file_buffer,
2022-07-17 01:26:02 +00:00
face_index,
script,
direction,
vertical,
read_scope_builder: |buffer| ReadScope::new(buffer),
2022-07-18 21:51:18 +00:00
font_data_builder: |scope| scope.read::<AllsortsFontData<'_>>().unwrap(),
inner_builder: |font_data| {
AllsortsFont::new(font_data.table_provider(0).unwrap())
.unwrap()
.unwrap()
},
}
2022-07-18 21:51:18 +00:00
.build()
}
2022-07-17 01:26:02 +00:00
pub fn shape(&mut self, text: &str) -> Vec<GlyphPosition> {
let face_index = *self.borrow_face_index() as u16;
let presentation = MatchingPresentation::Required;
let script = *self.borrow_script();
let lang_tag = None;
let features = allsorts::gsub::Features::default();
let kerning = true;
let direction = *self.borrow_direction();
let vertical = *self.borrow_vertical();
self.with_inner_mut(|font| {
let mapped = font.map_glyphs(text, script, presentation);
let infos = font
.shape(mapped, script, lang_tag, &features, kerning)
.unwrap();
let mut layout = GlyphLayout::new(font, &infos, direction, vertical);
let positions = layout.glyph_positions().unwrap();
let mut glyphs = Vec::with_capacity(positions.len());
for (glyph, position) in infos.iter().zip(&positions) {
glyphs.push(GlyphPosition {
face: face_index,
index: glyph.glyph.glyph_index,
hori_advance: position.hori_advance,
vert_advance: position.vert_advance,
xoff: position.x_offset,
yoff: position.y_offset,
});
}
glyphs
})
}
}
pub struct FontStore {
source: Box<dyn font_kit::source::Source>,
2022-07-16 17:44:22 +00:00
}
impl FontStore {
pub fn new() -> Self {
let source = font_kit::source::SystemSource::new();
let source = Box::new(source);
Self { source }
}
pub fn load_font(&self, title: &str) -> Font {
use font_kit::family_name::FamilyName;
use font_kit::handle::Handle;
2022-07-18 21:51:18 +00:00
use font_kit::properties::Properties;
println!("loading font family {}", title);
let font_handle = self
.source
.select_best_match(&[FamilyName::Title(title.to_string())], &Properties::new())
.unwrap();
println!("loading font file: {:?}", font_handle);
let font_data = if let Handle::Path {
path,
font_index: _,
} = font_handle
{
std::fs::read(path).unwrap()
} else {
panic!("font is not a file for some reason");
};
2022-07-16 20:06:06 +00:00
let script = tag::LATN;
2022-07-17 01:26:02 +00:00
let face_index = 0;
let direction = TextDirection::LeftToRight;
let vertical = false;
Font::load(face_index, script, direction, vertical, font_data.clone())
}
}
2022-07-16 17:44:22 +00:00
pub struct DummyText {
vertices: Vec<MeshVertex>,
indices: Vec<MeshIndex>,
}
impl Default for DummyText {
fn default() -> Self {
let store = FontStore::new();
let text = "toki o! nimi mi li jan [_mun_alasa_sona]. toki+pona li pona.";
let font_families = ["mononoki", "Liberation Sans", "Ubuntu"];
2022-07-16 17:44:22 +00:00
let mut mesh = Self {
vertices: Vec::new(),
indices: Vec::new(),
};
let line_height = -0.1;
let mut line_offset = 0.9;
for family in font_families.iter() {
let font = store.load_font(family);
let glyphs = font.shape(text);
font.draw(&mut mesh, &glyphs, line_offset);
line_offset += line_height;
2022-07-16 17:44:22 +00:00
}
mesh
}
}
impl DummyText {
pub fn draw(&self, cmds: &mut Vec<DrawCommand>) {
cmds.push(DrawCommand::Mesh {
vertices: self.vertices.clone(),
indices: self.indices.clone(),
});
}
}
pub struct OutlineSink {
units_per_em: f32,
2022-07-16 17:44:22 +00:00
builder: lyon::path::path::Builder,
}
impl OutlineSink {
pub fn new(units_per_em: f32) -> Self {
2022-07-16 17:44:22 +00:00
Self {
units_per_em,
2022-07-16 17:44:22 +00:00
builder: lyon::path::Path::builder(),
}
}
fn pf_line_to_lyon(
&self,
line: &LineSegment2F,
) -> (lyon::geom::Point<f32>, lyon::geom::Point<f32>) {
2022-07-16 17:44:22 +00:00
(
self.pf_vector_to_lyon(&line.from()),
self.pf_vector_to_lyon(&line.to()),
2022-07-16 17:44:22 +00:00
)
}
fn pf_vector_to_lyon(&self, v: &Vector2F) -> lyon::geom::Point<f32> {
lyon::geom::Point::<f32>::new(v.x(), v.y()) / self.units_per_em
2022-07-16 17:44:22 +00:00
}
}
impl allsorts::outline::OutlineSink for OutlineSink {
fn move_to(&mut self, to: Vector2F) {
let to = self.pf_vector_to_lyon(&to);
2022-07-16 17:44:22 +00:00
self.builder.begin(to);
}
fn line_to(&mut self, to: Vector2F) {
let to = self.pf_vector_to_lyon(&to);
2022-07-16 17:44:22 +00:00
self.builder.line_to(to);
}
fn quadratic_curve_to(&mut self, control: Vector2F, to: Vector2F) {
let control = self.pf_vector_to_lyon(&control);
let to = self.pf_vector_to_lyon(&to);
2022-07-16 17:44:22 +00:00
self.builder.quadratic_bezier_to(control, to);
}
fn cubic_curve_to(&mut self, control: LineSegment2F, to: Vector2F) {
let (ctrl1, ctrl2) = self.pf_line_to_lyon(&control);
let to = self.pf_vector_to_lyon(&to);
2022-07-16 17:44:22 +00:00
self.builder.cubic_bezier_to(ctrl1, ctrl2, to);
}
fn close(&mut self) {
self.builder.close();
}
}