summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-09-19 17:44:40 +0200
committerLaurenz <laurmaedje@gmail.com>2022-09-19 17:44:40 +0200
commite29f55bb294cc298daad97accf6d8a76976b409c (patch)
treecc4db3b61fa23e13f781e992c63427d36e77ef8c /src/library
parent59f67b79c7ff50f0bc9a27373d0fa36d1523e08a (diff)
Remove font store
Diffstat (limited to 'src/library')
-rw-r--r--src/library/graphics/image.rs2
-rw-r--r--src/library/math/rex.rs15
-rw-r--r--src/library/prelude.rs1
-rw-r--r--src/library/text/deco.rs11
-rw-r--r--src/library/text/mod.rs4
-rw-r--r--src/library/text/par.rs43
-rw-r--r--src/library/text/shaping.rs92
-rw-r--r--src/library/text/shift.rs14
-rw-r--r--src/library/utility/data.rs2
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);