use super::{DrawCommand, MeshIndex, MeshVertex}; use allsorts::binary::read::ReadScope; use allsorts::cff::CFF; use allsorts::font::{GlyphTableFlags, MatchingPresentation}; use allsorts::font_data::{DynamicFontTableProvider, FontData}; 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}; use canary_types::GlyphPosition; use lyon::path::Path; use ouroboros::self_referencing; const TEXT_SCALE: f32 = 0.0001; pub struct GlyphCache { glyphs: Vec, } impl GlyphCache { pub fn new(font: &AllsortsFont) -> Self { let glyph_num = font.num_glyphs(); let mut glyphs = Vec::with_capacity(glyph_num as usize); 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::>().unwrap(); for index in 0..glyph_num { glyphs.push(CachedGlyph::new(&mut cff, index)); } } 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::>(( 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::>(&loca) .unwrap(); for index in 0..glyph_num { glyphs.push(CachedGlyph::new(&mut glyf, index)); } } else { panic!("no glyf or CFF table"); } Self { glyphs } } pub fn draw(&mut self, mesh: &mut DummyText, index: u16, xpos: i32, ypos: i32) { let glyph = self.glyphs.get_mut(index as usize).unwrap().get_mesh(); let voff = mesh.vertices.len() as MeshIndex; let xoff = (xpos as f32) * TEXT_SCALE; let yoff = (ypos as f32) * TEXT_SCALE; for v in glyph.vertices.iter() { let x = v.position.x + xoff - 0.9; let y = v.position.y + yoff + 0.5; mesh.vertices.push(MeshVertex { position: canary_types::Vec2 { x, y }, color: v.color, }); } for i in glyph.indices.iter() { mesh.indices.push(i + voff); } } } pub struct CachedGlyph { path: Path, mesh: Option, } impl CachedGlyph { pub fn new(builder: &mut impl OutlineBuilder, index: u16) -> Self { let mut sink = OutlineSink::new(); builder.visit(index, &mut sink).unwrap(); let path = sink.builder.build(); Self { path, mesh: None } } pub fn get_mesh(&mut self) -> &GlyphMesh { self.mesh.get_or_insert_with(|| { use lyon::tessellation::*; let mut geometry: VertexBuffers = VertexBuffers::new(); let mut tessellator = FillTessellator::new(); tessellator .tessellate_path( &self.path.clone(), &FillOptions::default(), &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(); GlyphMesh { vertices: geometry.vertices, indices: geometry.indices, } }) } } pub struct GlyphMesh { vertices: Vec, indices: Vec, } #[self_referencing] pub struct Font { file_buffer: Vec, face_index: u16, script: u32, direction: TextDirection, vertical: bool, #[borrows(file_buffer)] #[covariant] read_scope: ReadScope<'this>, #[borrows(read_scope)] #[not_covariant] font_data: FontData<'this>, #[borrows(font_data)] #[covariant] inner: AllsortsFont>, } impl Font { pub fn load( face_index: u16, script: u32, direction: TextDirection, vertical: bool, file_buffer: Vec, ) -> Self { FontBuilder { file_buffer, face_index, script, direction, vertical, read_scope_builder: |buffer| ReadScope::new(buffer), font_data_builder: |scope| scope.read::>().unwrap(), inner_builder: |font_data| { AllsortsFont::new(font_data.table_provider(0).unwrap()) .unwrap() .unwrap() }, } .build() } pub fn create_glyph_cache(&self) -> GlyphCache { GlyphCache::new(self.borrow_inner()) } pub fn shape(&mut self, text: &str) -> Vec { 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 DummyText { vertices: Vec, indices: Vec, } impl Default for DummyText { fn default() -> Self { let script = tag::LATN; let face_index = 0; let direction = TextDirection::LeftToRight; let vertical = false; let buffer = include_bytes!("linja-pona-4.9.otf"); let mut font = Font::load(face_index, script, direction, vertical, buffer.to_vec()); let mut cache = font.create_glyph_cache(); let text = "toki o! nimi mi li [_mun_alasa_sona]. toki+pona li pona."; let glyphs = font.shape(text); let mut mesh = Self { vertices: Vec::new(), indices: Vec::new(), }; let mut xcur = 0; let mut ycur = 0; for glyph in glyphs.iter() { let xpos = xcur + glyph.xoff; let ypos = ycur + glyph.yoff; xcur += glyph.hori_advance; ycur += glyph.vert_advance; cache.draw(&mut mesh, glyph.index, xpos, ypos); } mesh } } impl DummyText { pub fn draw(&self, cmds: &mut Vec) { cmds.push(DrawCommand::Mesh { vertices: self.vertices.clone(), indices: self.indices.clone(), }); } } pub struct OutlineSink { builder: lyon::path::path::Builder, } impl OutlineSink { pub fn new() -> Self { Self { builder: lyon::path::Path::builder(), } } fn pf_line_to_lyon(line: &LineSegment2F) -> (lyon::geom::Point, lyon::geom::Point) { ( Self::pf_vector_to_lyon(&line.from()), Self::pf_vector_to_lyon(&line.to()), ) } fn pf_vector_to_lyon(v: &Vector2F) -> lyon::geom::Point { lyon::geom::Point::::new(v.x(), v.y()) } } impl allsorts::outline::OutlineSink for OutlineSink { fn move_to(&mut self, to: Vector2F) { let to = Self::pf_vector_to_lyon(&to); self.builder.begin(to); } fn line_to(&mut self, to: Vector2F) { let to = Self::pf_vector_to_lyon(&to); 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); 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); self.builder.cubic_bezier_to(ctrl1, ctrl2, to); } fn close(&mut self) { self.builder.close(); } }