diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-09-19 17:44:40 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-09-19 17:44:40 +0200 |
| commit | e29f55bb294cc298daad97accf6d8a76976b409c (patch) | |
| tree | cc4db3b61fa23e13f781e992c63427d36e77ef8c /src/library | |
| parent | 59f67b79c7ff50f0bc9a27373d0fa36d1523e08a (diff) | |
Remove font store
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/graphics/image.rs | 2 | ||||
| -rw-r--r-- | src/library/math/rex.rs | 15 | ||||
| -rw-r--r-- | src/library/prelude.rs | 1 | ||||
| -rw-r--r-- | src/library/text/deco.rs | 11 | ||||
| -rw-r--r-- | src/library/text/mod.rs | 4 | ||||
| -rw-r--r-- | src/library/text/par.rs | 43 | ||||
| -rw-r--r-- | src/library/text/shaping.rs | 92 | ||||
| -rw-r--r-- | src/library/text/shift.rs | 14 | ||||
| -rw-r--r-- | src/library/utility/data.rs | 2 |
9 files changed, 95 insertions, 89 deletions
diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs index 784afa01..f8cdc4cd 100644 --- a/src/library/graphics/image.rs +++ b/src/library/graphics/image.rs @@ -22,7 +22,7 @@ impl ImageNode { let image = vm .ctx .loader - .load(&full) + .file(&full) .and_then(|buffer| Image::new(buffer, ext)) .map_err(|err| failed_to_load("image", &full, err)) .at(span)?; diff --git a/src/library/math/rex.rs b/src/library/math/rex.rs index 165642d3..0116b4b2 100644 --- a/src/library/math/rex.rs +++ b/src/library/math/rex.rs @@ -4,7 +4,7 @@ use rex::layout::{LayoutSettings, Style}; use rex::parser::color::RGBA; use rex::render::{Backend, Cursor, Renderer}; -use crate::font::FontId; +use crate::font::Font; use crate::library::prelude::*; use crate::library::text::{variant, FontFamily, Lang, TextNode}; @@ -28,14 +28,15 @@ impl Layout for RexNode { ) -> TypResult<Vec<Frame>> { // Load the font. let span = self.tex.span; - let font_id = ctx - .fonts + let font = ctx + .loader + .book() .select(self.family.as_str(), variant(styles)) + .and_then(|id| ctx.loader.font(id).ok()) .ok_or("failed to find math font") .at(span)?; // Prepare the font context. - let font = ctx.fonts.get(font_id); let ctx = font .math() .map(|math| FontContext::new(font.ttf(), math)) @@ -76,7 +77,7 @@ impl Layout for RexNode { frame }, baseline: top, - font_id, + font: font.clone(), fill: styles.get(TextNode::FILL), lang: styles.get(TextNode::LANG), colors: vec![], @@ -93,7 +94,7 @@ impl Layout for RexNode { struct FrameBackend { frame: Frame, baseline: Length, - font_id: FontId, + font: Font, fill: Paint, lang: Lang, colors: Vec<RGBA>, @@ -119,7 +120,7 @@ impl Backend for FrameBackend { self.frame.push( self.transform(pos), Element::Text(Text { - font_id: self.font_id, + font: self.font.clone(), size: Length::pt(scale), fill: self.fill(), lang: self.lang, diff --git a/src/library/prelude.rs b/src/library/prelude.rs index f55447c3..8bd3b815 100644 --- a/src/library/prelude.rs +++ b/src/library/prelude.rs @@ -17,6 +17,7 @@ pub use crate::eval::{ }; pub use crate::frame::*; pub use crate::geom::*; +pub use crate::loading::Loader; pub use crate::model::{ Content, Fold, Key, Layout, LayoutNode, Regions, Resolve, Selector, Show, ShowNode, StyleChain, StyleMap, StyleVec, diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs index 6d8b2854..8295f1a2 100644 --- a/src/library/text/deco.rs +++ b/src/library/text/deco.rs @@ -2,7 +2,6 @@ use kurbo::{BezPath, Line, ParamCurve}; use ttf_parser::{GlyphId, OutlineBuilder}; use super::TextNode; -use crate::font::FontStore; use crate::library::prelude::*; /// Typeset underline, stricken-through or overlined text. @@ -88,14 +87,12 @@ pub const OVERLINE: DecoLine = 2; pub fn decorate( frame: &mut Frame, deco: &Decoration, - fonts: &FontStore, text: &Text, shift: Length, pos: Point, width: Length, ) { - let font = fonts.get(text.font_id); - let font_metrics = font.metrics(); + let font_metrics = text.font.metrics(); let metrics = match deco.line { STRIKETHROUGH => font_metrics.strikethrough, OVERLINE => font_metrics.overline, @@ -143,7 +140,7 @@ pub fn decorate( let mut builder = BezPathBuilder::new(font_metrics.units_per_em, text.size, dx.to_raw()); - let bbox = font.ttf().outline_glyph(GlyphId(glyph.id), &mut builder); + let bbox = text.font.ttf().outline_glyph(GlyphId(glyph.id), &mut builder); let path = builder.finish(); x += glyph.x_advance.at(text.size); @@ -151,8 +148,8 @@ pub fn decorate( // Only do the costly segments intersection test if the line // intersects the bounding box. if bbox.map_or(false, |bbox| { - let y_min = -font.to_em(bbox.y_max).at(text.size); - let y_max = -font.to_em(bbox.y_min).at(text.size); + let y_min = -text.font.to_em(bbox.y_max).at(text.size); + let y_max = -text.font.to_em(bbox.y_min).at(text.size); offset >= y_min && offset <= y_max }) { diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index 9c4f33f1..35780186 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -24,9 +24,7 @@ use std::borrow::Cow; use ttf_parser::Tag; -use crate::font::{ - Font, FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric, -}; +use crate::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric}; use crate::library::prelude::*; use crate::util::EcoString; diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 168aca26..8309bcc8 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -5,7 +5,6 @@ use unicode_script::{Script, UnicodeScript}; use xi_unicode::LineBreakIterator; use super::{shape, Lang, Quoter, Quotes, RepeatNode, ShapedText, TextNode}; -use crate::font::FontStore; use crate::library::layout::Spacing; use crate::library::prelude::*; use crate::util::EcoString; @@ -78,7 +77,7 @@ impl Layout for ParNode { let p = prepare(ctx, self, &text, segments, regions, styles)?; // Break the paragraph into lines. - let lines = linebreak(&p, &mut ctx.fonts, regions.first.x); + let lines = linebreak(&p, ctx.loader.as_ref(), regions.first.x); // Stack the lines into one frame per region. stack(&p, ctx, &lines, regions) @@ -518,7 +517,13 @@ fn prepare<'a>( let end = cursor + segment.len(); match segment { Segment::Text(_) => { - shape_range(&mut items, &mut ctx.fonts, &bidi, cursor .. end, styles); + shape_range( + &mut items, + ctx.loader.as_ref(), + &bidi, + cursor .. end, + styles, + ); } Segment::Spacing(spacing) => match spacing { Spacing::Relative(v) => { @@ -562,14 +567,14 @@ fn prepare<'a>( /// items for them. fn shape_range<'a>( items: &mut Vec<Item<'a>>, - fonts: &mut FontStore, + loader: &dyn Loader, bidi: &BidiInfo<'a>, range: Range, styles: StyleChain<'a>, ) { let mut process = |text, level: Level| { let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL }; - let shaped = shape(fonts, text, styles, dir); + let shaped = shape(loader, text, styles, dir); items.push(Item::Text(shaped)); }; @@ -628,12 +633,12 @@ fn shared_get<'a, K: Key<'a>>( /// Find suitable linebreaks. fn linebreak<'a>( p: &'a Preparation<'a>, - fonts: &mut FontStore, + loader: &dyn Loader, width: Length, ) -> Vec<Line<'a>> { match p.styles.get(ParNode::LINEBREAKS) { - Linebreaks::Simple => linebreak_simple(p, fonts, width), - Linebreaks::Optimized => linebreak_optimized(p, fonts, width), + Linebreaks::Simple => linebreak_simple(p, loader, width), + Linebreaks::Optimized => linebreak_optimized(p, loader, width), } } @@ -642,7 +647,7 @@ fn linebreak<'a>( /// very unbalanced line, but is fast and simple. fn linebreak_simple<'a>( p: &'a Preparation<'a>, - fonts: &mut FontStore, + loader: &dyn Loader, width: Length, ) -> Vec<Line<'a>> { let mut lines = vec![]; @@ -651,7 +656,7 @@ fn linebreak_simple<'a>( for (end, mandatory, hyphen) in breakpoints(p) { // Compute the line and its size. - let mut attempt = line(p, fonts, start .. end, mandatory, hyphen); + let mut attempt = line(p, loader, start .. end, mandatory, hyphen); // If the line doesn't fit anymore, we push the last fitting attempt // into the stack and rebuild the line from the attempt's end. The @@ -660,7 +665,7 @@ fn linebreak_simple<'a>( if let Some((last_attempt, last_end)) = last.take() { lines.push(last_attempt); start = last_end; - attempt = line(p, fonts, start .. end, mandatory, hyphen); + attempt = line(p, loader, start .. end, mandatory, hyphen); } } @@ -702,7 +707,7 @@ fn linebreak_simple<'a>( /// text. fn linebreak_optimized<'a>( p: &'a Preparation<'a>, - fonts: &mut FontStore, + loader: &dyn Loader, width: Length, ) -> Vec<Line<'a>> { /// The cost of a line or paragraph layout. @@ -727,7 +732,7 @@ fn linebreak_optimized<'a>( let mut table = vec![Entry { pred: 0, total: 0.0, - line: line(p, fonts, 0 .. 0, false, false), + line: line(p, loader, 0 .. 0, false, false), }]; let em = p.styles.get(TextNode::SIZE); @@ -741,7 +746,7 @@ fn linebreak_optimized<'a>( for (i, pred) in table.iter_mut().enumerate().skip(active) { // Layout the line. let start = pred.line.end; - let attempt = line(p, fonts, start .. end, mandatory, hyphen); + let attempt = line(p, loader, start .. end, mandatory, hyphen); // Determine how much the line's spaces would need to be stretched // to make it the desired width. @@ -915,7 +920,7 @@ impl Breakpoints<'_> { /// Create a line which spans the given range. fn line<'a>( p: &'a Preparation, - fonts: &mut FontStore, + loader: &dyn Loader, mut range: Range, mandatory: bool, hyphen: bool, @@ -970,9 +975,9 @@ fn line<'a>( if hyphen || start + shaped.text.len() > range.end { if hyphen || start < range.end || before.is_empty() { let shifted = start - base .. range.end - base; - let mut reshaped = shaped.reshape(fonts, shifted); + let mut reshaped = shaped.reshape(loader, shifted); if hyphen || shy { - reshaped.push_hyphen(fonts); + reshaped.push_hyphen(loader); } width += reshaped.width; last = Some(Item::Text(reshaped)); @@ -993,7 +998,7 @@ fn line<'a>( if range.start + shaped.text.len() > end { if range.start < end { let shifted = range.start - base .. end - base; - let reshaped = shaped.reshape(fonts, shifted); + let reshaped = shaped.reshape(loader, shifted); width += reshaped.width; first = Some(Item::Text(reshaped)); } @@ -1144,7 +1149,7 @@ fn commit( offset += v.share(fr, remaining); } Item::Text(shaped) => { - let frame = shaped.build(&mut ctx.fonts, justification); + let frame = shaped.build(ctx.loader.as_ref(), justification); push(&mut offset, frame); } Item::Frame(frame) => { diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs index 4f7647d4..3d905c99 100644 --- a/src/library/text/shaping.rs +++ b/src/library/text/shaping.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use rustybuzz::{Feature, UnicodeBuffer}; use super::*; -use crate::font::{FontId, FontStore, FontVariant}; +use crate::font::{Font, FontVariant}; use crate::library::prelude::*; use crate::util::SliceExt; @@ -31,10 +31,10 @@ pub struct ShapedText<'a> { } /// A single glyph resulting from shaping. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct ShapedGlyph { /// The font the glyph is contained in. - pub font_id: FontId, + pub font: Font, /// The glyph's index in the font. pub glyph_id: u16, /// The advance width of the glyph. @@ -80,8 +80,8 @@ impl<'a> ShapedText<'a> { /// /// The `justification` defines how much extra advance width each /// [justifiable glyph](ShapedGlyph::is_justifiable) will get. - pub fn build(&self, fonts: &mut FontStore, justification: Length) -> Frame { - let (top, bottom) = self.measure(fonts); + pub fn build(&self, loader: &dyn Loader, justification: Length) -> Frame { + let (top, bottom) = self.measure(loader); let size = Size::new(self.width, top + bottom); let mut offset = Length::zero(); @@ -94,8 +94,8 @@ impl<'a> ShapedText<'a> { let fill = self.styles.get(TextNode::FILL); let link = self.styles.get(TextNode::LINK); - for ((font_id, y_offset), group) in - self.glyphs.as_ref().group_by_key(|g| (g.font_id, g.y_offset)) + for ((font, y_offset), group) in + self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset)) { let pos = Point::new(offset, top + shift + y_offset.at(self.size)); @@ -116,7 +116,7 @@ impl<'a> ShapedText<'a> { .collect(); let text = Text { - font_id, + font, size: self.size, lang, fill, @@ -128,7 +128,7 @@ impl<'a> ShapedText<'a> { // Apply line decorations. for deco in &decos { - decorate(&mut frame, &deco, fonts, &text, shift, pos, width); + decorate(&mut frame, &deco, &text, shift, pos, width); } frame.insert(text_layer, pos, Element::Text(text)); @@ -144,7 +144,7 @@ impl<'a> ShapedText<'a> { } /// Measure the top and bottom extent of this text. - fn measure(&self, fonts: &mut FontStore) -> (Length, Length) { + fn measure(&self, loader: &dyn Loader) -> (Length, Length) { let mut top = Length::zero(); let mut bottom = Length::zero(); @@ -162,14 +162,18 @@ impl<'a> ShapedText<'a> { // When there are no glyphs, we just use the vertical metrics of the // first available font. for family in families(self.styles) { - if let Some(font_id) = fonts.select(family, self.variant) { - expand(fonts.get(font_id)); + if let Some(font) = loader + .book() + .select(family, self.variant) + .and_then(|id| loader.font(id).ok()) + { + expand(&font); break; } } } else { - for (font_id, _) in self.glyphs.group_by_key(|g| g.font_id) { - expand(fonts.get(font_id)); + for g in self.glyphs.iter() { + expand(&g.font); } } @@ -195,7 +199,7 @@ impl<'a> ShapedText<'a> { /// shaping process if possible. pub fn reshape( &'a self, - fonts: &mut FontStore, + loader: &dyn Loader, text_range: Range<usize>, ) -> ShapedText<'a> { if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) { @@ -209,22 +213,24 @@ impl<'a> ShapedText<'a> { glyphs: Cow::Borrowed(glyphs), } } else { - shape(fonts, &self.text[text_range], self.styles, self.dir) + shape(loader, &self.text[text_range], self.styles, self.dir) } } /// Push a hyphen to end of the text. - pub fn push_hyphen(&mut self, fonts: &mut FontStore) { + pub fn push_hyphen(&mut self, loader: &dyn Loader) { families(self.styles).find_map(|family| { - let font_id = fonts.select(family, self.variant)?; - let font = fonts.get(font_id); + let font = loader + .book() + .select(family, self.variant) + .and_then(|id| loader.font(id).ok())?; let ttf = font.ttf(); let glyph_id = ttf.glyph_index('-')?; let x_advance = font.to_em(ttf.glyph_hor_advance(glyph_id)?); let cluster = self.glyphs.last().map(|g| g.cluster).unwrap_or_default(); self.width += x_advance.at(self.size); self.glyphs.to_mut().push(ShapedGlyph { - font_id, + font, glyph_id: glyph_id.0, x_advance, x_offset: Em::zero(), @@ -300,9 +306,9 @@ impl Debug for ShapedText<'_> { /// Holds shaping results and metadata common to all shaped segments. struct ShapingContext<'a> { - fonts: &'a mut FontStore, + loader: &'a dyn Loader, glyphs: Vec<ShapedGlyph>, - used: Vec<FontId>, + used: Vec<Font>, styles: StyleChain<'a>, size: Length, variant: FontVariant, @@ -313,7 +319,7 @@ struct ShapingContext<'a> { /// Shape text into [`ShapedText`]. pub fn shape<'a>( - fonts: &mut FontStore, + loader: &dyn Loader, text: &'a str, styles: StyleChain<'a>, dir: Dir, @@ -321,7 +327,7 @@ pub fn shape<'a>( let size = styles.get(TextNode::SIZE); let mut ctx = ShapingContext { - fonts, + loader, size, glyphs: vec![], used: vec![], @@ -362,32 +368,33 @@ fn shape_segment<'a>( } // Find the next available family. + let book = ctx.loader.book(); let mut selection = families.find_map(|family| { - ctx.fonts - .select(family, ctx.variant) - .filter(|id| !ctx.used.contains(id)) + book.select(family, ctx.variant) + .and_then(|id| ctx.loader.font(id).ok()) + .filter(|font| !ctx.used.contains(font)) }); // Do font fallback if the families are exhausted and fallback is enabled. if selection.is_none() && ctx.fallback { - let first = ctx.used.first().copied(); - selection = ctx - .fonts + let first = ctx.used.first().map(Font::info); + selection = book .select_fallback(first, ctx.variant, text) - .filter(|id| !ctx.used.contains(id)); + .and_then(|id| ctx.loader.font(id).ok()) + .filter(|font| !ctx.used.contains(font)); } // Extract the font id or shape notdef glyphs if we couldn't find any font. - let font_id = if let Some(id) = selection { - id + let font = if let Some(font) = selection { + font } else { - if let Some(&font_id) = ctx.used.first() { - shape_tofus(ctx, base, text, font_id); + if let Some(font) = ctx.used.first().cloned() { + shape_tofus(ctx, base, text, font); } return; }; - ctx.used.push(font_id); + ctx.used.push(font.clone()); // Fill the buffer with our text. let mut buffer = UnicodeBuffer::new(); @@ -400,7 +407,6 @@ fn shape_segment<'a>( }); // Shape! - let mut font = ctx.fonts.get(font_id); let buffer = rustybuzz::shape(font.ttf(), &ctx.tags, buffer); let infos = buffer.glyph_infos(); let pos = buffer.glyph_positions(); @@ -416,7 +422,7 @@ fn shape_segment<'a>( // Add the glyph to the shaped output. // TODO: Don't ignore y_advance. ctx.glyphs.push(ShapedGlyph { - font_id, + font: font.clone(), glyph_id: info.glyph_id as u16, x_advance: font.to_em(pos[i].x_advance), x_offset: font.to_em(pos[i].x_offset), @@ -471,8 +477,6 @@ fn shape_segment<'a>( // Recursively shape the tofu sequence with the next family. shape_segment(ctx, base + range.start, &text[range], families.clone()); - - font = ctx.fonts.get(font_id); } i += 1; @@ -482,12 +486,11 @@ fn shape_segment<'a>( } /// Shape the text with tofus from the given font. -fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font_id: FontId) { - let font = ctx.fonts.get(font_id); +fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font: Font) { let x_advance = font.advance(0).unwrap_or_default(); for (cluster, c) in text.char_indices() { ctx.glyphs.push(ShapedGlyph { - font_id, + font: font.clone(), glyph_id: 0, x_advance, x_offset: Em::zero(), @@ -511,8 +514,7 @@ fn track_and_space(ctx: &mut ShapingContext) { while let Some(glyph) = glyphs.next() { // Make non-breaking space same width as normal space. if glyph.c == '\u{00A0}' { - let font = ctx.fonts.get(glyph.font_id); - glyph.x_advance -= nbsp_delta(font).unwrap_or_default(); + glyph.x_advance -= nbsp_delta(&glyph.font).unwrap_or_default(); } if glyph.is_space() { diff --git a/src/library/text/shift.rs b/src/library/text/shift.rs index fde969d3..75b2a579 100644 --- a/src/library/text/shift.rs +++ b/src/library/text/shift.rs @@ -1,5 +1,4 @@ use super::{variant, TextNode, TextSize}; -use crate::font::FontStore; use crate::library::prelude::*; use crate::util::EcoString; @@ -47,7 +46,7 @@ impl<const S: ScriptKind> Show for ShiftNode<S> { let mut transformed = None; if styles.get(Self::TYPOGRAPHIC) { if let Some(text) = search_text(&self.0, S) { - if is_shapable(&mut ctx.fonts, &text, styles) { + if is_shapable(ctx.loader.as_ref(), &text, styles) { transformed = Some(Content::Text(text)); } } @@ -92,11 +91,14 @@ fn search_text(content: &Content, mode: ScriptKind) -> Option<EcoString> { /// Checks whether the first retrievable family contains all code points of the /// given string. -fn is_shapable(fonts: &mut FontStore, text: &str, styles: StyleChain) -> bool { +fn is_shapable(loader: &dyn Loader, text: &str, styles: StyleChain) -> bool { + let book = loader.book(); for family in styles.get(TextNode::FAMILY).iter() { - if let Some(font_id) = fonts.select(family.as_str(), variant(styles)) { - let ttf = fonts.get(font_id).ttf(); - return text.chars().all(|c| ttf.glyph_index(c).is_some()); + if let Some(font) = book + .select(family.as_str(), variant(styles)) + .and_then(|id| loader.font(id).ok()) + { + return text.chars().all(|c| font.ttf().glyph_index(c).is_some()); } } diff --git a/src/library/utility/data.rs b/src/library/utility/data.rs index 0f9e6bf0..f9e970dc 100644 --- a/src/library/utility/data.rs +++ b/src/library/utility/data.rs @@ -7,7 +7,7 @@ pub fn csv(vm: &mut Machine, args: &mut Args) -> TypResult<Value> { let path = vm.locate(&path).at(span)?; let try_load = || -> io::Result<Value> { - let data = vm.ctx.loader.load(&path)?; + let data = vm.ctx.loader.file(&path)?; let mut builder = csv::ReaderBuilder::new(); builder.has_headers(false); |
