diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index fce72585d..52200e661 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -3,10 +3,9 @@ use std::rc::Rc; use criterion::{criterion_group, criterion_main, Criterion}; use fontdock::fs::{FsIndex, FsProvider}; -use fontdock::FontLoader; -use typstc::font::DynProvider; -use typstc::syntax::parsing::parse; +use typstc::font::FontLoader; +use typstc::parse::parse; use typstc::Typesetter; const FONT_DIR: &str = "fonts"; @@ -25,10 +24,9 @@ fn typesetting_benchmark(c: &mut Criterion) { let mut index = FsIndex::new(); index.search_dir(FONT_DIR); - let (descriptors, files) = index.clone().into_vecs(); - let provider = FsProvider::new(files.clone()); - let dynamic = Box::new(provider) as Box; - let loader = FontLoader::new(dynamic, descriptors); + let (descriptors, files) = index.into_vecs(); + let provider = FsProvider::new(files); + let loader = FontLoader::new(Box::new(provider), descriptors); let loader = Rc::new(RefCell::new(loader)); let typesetter = Typesetter::new(loader.clone()); diff --git a/main/main.rs b/main/main.rs index a0ca9edd8..d3ad641bb 100644 --- a/main/main.rs +++ b/main/main.rs @@ -5,11 +5,10 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use fontdock::fs::{FsIndex, FsProvider}; -use fontdock::FontLoader; use futures_executor::block_on; use typstc::export::pdf; -use typstc::font::DynProvider; +use typstc::font::FontLoader; use typstc::Typesetter; fn main() { @@ -37,9 +36,8 @@ fn main() { index.search_os(); let (descriptors, files) = index.into_vecs(); - let provider = FsProvider::new(files.clone()); - let dynamic = Box::new(provider) as Box; - let loader = FontLoader::new(dynamic, descriptors); + let provider = FsProvider::new(files); + let loader = FontLoader::new(Box::new(provider), descriptors); let loader = Rc::new(RefCell::new(loader)); let typesetter = Typesetter::new(loader.clone()); @@ -62,8 +60,8 @@ fn main() { ); } + let loader = loader.borrow(); let file = File::create(&dest_path).expect("failed to create output file"); - let writer = BufWriter::new(file); pdf::export(&layouts, &loader, writer).expect("failed to export pdf"); } diff --git a/src/export/pdf.rs b/src/export/pdf.rs index c4c12713c..b87b31811 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -13,10 +13,10 @@ use tide::font::{ use tide::{PdfWriter, Rect, Ref, Trailer, Version}; use ttf_parser::{name_id, GlyphId}; +use crate::font::FontLoader; use crate::layout::elements::LayoutElement; use crate::layout::BoxLayout; use crate::length::Length; -use crate::SharedFontLoader; /// Export a list of layouts into a _PDF_ document. /// @@ -28,7 +28,7 @@ use crate::SharedFontLoader; /// bytes written. pub fn export( layout: &[BoxLayout], - loader: &SharedFontLoader, + loader: &FontLoader, target: W, ) -> io::Result { PdfExporter::new(layout, loader, target)?.write() @@ -37,7 +37,7 @@ pub fn export( struct PdfExporter<'a, W: Write> { writer: PdfWriter, layouts: &'a [BoxLayout], - loader: &'a SharedFontLoader, + loader: &'a FontLoader, /// We need to know exactly which indirect reference id will be used for /// which objects up-front to correctly declare the document catalogue, page /// tree and so on. These offsets are computed in the beginning and stored @@ -61,7 +61,7 @@ const NUM_OBJECTS_PER_FONT: u32 = 5; impl<'a, W: Write> PdfExporter<'a, W> { fn new( layouts: &'a [BoxLayout], - loader: &'a SharedFontLoader, + loader: &'a FontLoader, target: W, ) -> io::Result { let (to_pdf, to_fontdock) = remap_fonts(layouts); @@ -168,8 +168,8 @@ impl<'a, W: Write> PdfExporter<'a, W> { let mut id = self.offsets.fonts.0; for &face_id in &self.to_layout { - let loader = self.loader.borrow(); - let face = loader.get_loaded(face_id); + let owned_face = self.loader.get_loaded(face_id); + let face = owned_face.get(); let name = face .names() @@ -269,7 +269,7 @@ impl<'a, W: Write> PdfExporter<'a, W> { .write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?; // Write the face's bytes. - self.writer.write_obj(id + 4, &FontStream::new(face.data()))?; + self.writer.write_obj(id + 4, &FontStream::new(owned_face.data()))?; id += NUM_OBJECTS_PER_FONT; } diff --git a/src/font.rs b/src/font.rs index 1e34ac051..539f3188e 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,25 +1,34 @@ //! Font handling. use std::cell::RefCell; -use std::ops::Deref; use std::rc::Rc; -use fontdock::{ContainsChar, FaceFromVec, FontLoader, FontProvider}; +use fontdock::{ContainsChar, FaceFromVec, FontProvider}; use ttf_parser::Face; -/// A referenced-count shared font loader backed by a dynamic provider. -pub type SharedFontLoader = Rc>>>; +/// A reference-counted shared font loader backed by a dynamic font provider. +pub type SharedFontLoader = Rc>; -/// The dynamic font provider type backing the font loader. +/// A font loader backed by a dynamic provider. +pub type FontLoader = fontdock::FontLoader>; + +/// The dynamic font provider backing the font loader. pub type DynProvider = dyn FontProvider; /// An owned font face. pub struct OwnedFace { - data: Vec, + data: Box<[u8]>, face: Face<'static>, } impl OwnedFace { + /// Get a reference to the underlying face. + pub fn get<'a>(&'a self) -> &'a Face<'a> { + // We can't implement Deref because that would leak the internal 'static + // lifetime. + &self.face + } + /// The raw face data. pub fn data(&self) -> &[u8] { &self.data @@ -28,13 +37,15 @@ impl OwnedFace { impl FaceFromVec for OwnedFace { fn from_vec(vec: Vec, i: u32) -> Option { - // The vec's location is stable in memory since we don't touch it and - // it can't be touched from outside this type. + let data = vec.into_boxed_slice(); + + // SAFETY: The slices's location is stable in memory since we don't + // touch it and it can't be touched from outside this type. let slice: &'static [u8] = - unsafe { std::slice::from_raw_parts(vec.as_ptr(), vec.len()) }; + unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) }; Some(Self { - data: vec, + data, face: Face::from_slice(slice, i).ok()?, }) } @@ -42,14 +53,6 @@ impl FaceFromVec for OwnedFace { impl ContainsChar for OwnedFace { fn contains_char(&self, c: char) -> bool { - self.glyph_index(c).is_some() - } -} - -impl Deref for OwnedFace { - type Target = Face<'static>; - - fn deref(&self) -> &Self::Target { - &self.face + self.get().glyph_index(c).is_some() } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 971d6be67..7dd557c9e 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -9,7 +9,7 @@ use ttf_parser::GlyphId; use super::elements::{LayoutElement, Shaped}; use super::*; -use crate::font::SharedFontLoader; +use crate::font::FontLoader; use crate::geom::Size; use crate::style::TextStyle; @@ -30,11 +30,11 @@ struct TextLayouter<'a> { } /// The context for text layouting. -#[derive(Debug, Copy, Clone)] +#[derive(Debug)] pub struct TextContext<'a> { /// The font loader to retrieve fonts from when typesetting text with /// `layout_text`. - pub loader: &'a SharedFontLoader, + pub loader: &'a mut FontLoader, /// The style for text: Font selection with classes, weights and variants, /// font sizes, spacing and so on. pub style: &'a TextStyle, @@ -50,12 +50,12 @@ pub struct TextContext<'a> { impl<'a> TextLayouter<'a> { fn new(text: &'a str, ctx: TextContext<'a>) -> Self { Self { - ctx, text, shaped: Shaped::new(FaceId::MAX, ctx.style.font_size()), elements: LayoutElements::new(), start: 0.0, width: 0.0, + ctx, } } @@ -116,7 +116,6 @@ impl<'a> TextLayouter<'a> { } async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> { - let mut loader = self.ctx.loader.borrow_mut(); let mut variant = self.ctx.style.variant; if self.ctx.style.bolder { @@ -137,7 +136,9 @@ impl<'a> TextLayouter<'a> { c, }; - if let Some((id, face)) = loader.query(query).await { + if let Some((id, owned_face)) = self.ctx.loader.query(query).await { + let face = owned_face.get(); + // Determine the width of the char. let units_per_em = face.units_per_em().unwrap_or(1000) as f64; let ratio = 1.0 / units_per_em; diff --git a/src/layout/tree.rs b/src/layout/tree.rs index b6f9ab47f..19bff091a 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -107,7 +107,7 @@ impl<'a> TreeLayouter<'a> { async fn layout_text(&mut self, text: &str) { self.layouter.add( layout_text(text, TextContext { - loader: &self.ctx.loader, + loader: &mut self.ctx.loader.borrow_mut(), style: &self.style.text, dir: self.ctx.axes.primary, align: self.ctx.align, diff --git a/tests/test_typeset.rs b/tests/test_typeset.rs index c7f4cd5de..e9051d104 100644 --- a/tests/test_typeset.rs +++ b/tests/test_typeset.rs @@ -7,13 +7,12 @@ use std::path::Path; use std::rc::Rc; use fontdock::fs::{FsIndex, FsProvider}; -use fontdock::FontLoader; use futures_executor::block_on; use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector}; use ttf_parser::OutlineBuilder; use typstc::export::pdf; -use typstc::font::{DynProvider, SharedFontLoader}; +use typstc::font::{FontLoader, SharedFontLoader}; use typstc::geom::{Size, Value4}; use typstc::layout::elements::{LayoutElement, Shaped}; use typstc::layout::MultiLayout; @@ -60,13 +59,12 @@ fn main() { let mut index = FsIndex::new(); index.search_dir(FONT_DIR); - let (descriptors, files) = index.clone().into_vecs(); - let provider = FsProvider::new(files.clone()); - let dynamic = Box::new(provider) as Box; - let loader = FontLoader::new(dynamic, descriptors); + let (descriptors, files) = index.into_vecs(); + let provider = FsProvider::new(files); + let loader = FontLoader::new(Box::new(provider), descriptors); let loader = Rc::new(RefCell::new(loader)); - let mut typesetter = Typesetter::new(loader.clone()); + let mut typesetter = Typesetter::new(loader.clone()); typesetter.set_page_style(PageStyle { class: PaperClass::Custom, size: Size::with_all(Length::pt(250.0).as_raw()), @@ -106,6 +104,8 @@ fn test( ); } + let loader = loader.borrow(); + let png_path = format!("{}/{}.png", OUT_DIR, name); render(&layouts, &loader, 3.0).write_png(png_path).unwrap(); @@ -144,7 +144,7 @@ impl TestFilter { } } -fn render(layouts: &MultiLayout, loader: &SharedFontLoader, scale: f64) -> DrawTarget { +fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget { let pad = scale * 10.0; let width = 2.0 * pad + layouts @@ -191,13 +191,12 @@ fn render(layouts: &MultiLayout, loader: &SharedFontLoader, scale: f64) -> DrawT fn render_shaped( surface: &mut DrawTarget, - loader: &SharedFontLoader, + loader: &FontLoader, shaped: &Shaped, pos: Size, scale: f64, ) { - let loader = loader.borrow(); - let face = loader.get_loaded(shaped.face); + let face = loader.get_loaded(shaped.face).get(); for (&glyph, &offset) in shaped.glyphs.iter().zip(&shaped.offsets) { let mut builder = WrappedPathBuilder(PathBuilder::new());