Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Add font objects #4093

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/typst/src/foundations/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::foundations::{
};
use crate::introspection::Locatable;
use crate::syntax::Span;
use crate::text::{FontFamily, FontList, TextElem};
use crate::text::{FontList, FontListEntry, TextElem};
use crate::utils::LazyHash;

/// Provides access to active styles.
Expand Down Expand Up @@ -139,7 +139,7 @@ impl Styles {

/// Set a font family composed of a preferred family and existing families
/// from a style chain.
pub fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) {
pub fn set_family(&mut self, preferred: FontListEntry, existing: StyleChain) {
self.set(TextElem::set_font(FontList(
std::iter::once(preferred)
.chain(TextElem::font_in(existing).into_iter().cloned())
Expand Down
34 changes: 26 additions & 8 deletions crates/typst/src/layout/inline/shaping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use crate::foundations::StyleChain;
use crate::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size};
use crate::syntax::Span;
use crate::text::{
decorate, families, features, variant, Font, FontVariant, Glyph, Lang, Region,
TextElem, TextItem,
decorate, families, features, font_list_entries, variant, Font, FontListEntry,
FontVariant, Glyph, Lang, Region, TextElem, TextItem,
};
use crate::utils::SliceExt;
use crate::World;
Expand Down Expand Up @@ -591,15 +591,30 @@ impl Debug for ShapedText<'_> {

/// Holds shaping results and metadata common to all shaped segments.
struct ShapingContext<'a, 'v> {
/// The parent [`Engine`].
engine: &'a Engine<'v>,
/// The parent [`SpanMapper`].
spans: &'a SpanMapper,
/// The list of glyphs in the output.
glyphs: Vec<ShapedGlyph>,
/// A stack of fonts that are already used.
///
/// This is used by [`shape_segment`], which calls itself recursively
/// to shape text that could not be shaped in the first pass.
used: Vec<Font>,
/// The style chain for this context.
styles: StyleChain<'a>,
/// The size of the text.
size: Abs,
/// The font variant used for selecting a font.
variant: FontVariant,
/// A list of base features.
///
/// This does not include any features listed in a [`FontListEntry`].
features: Vec<rustybuzz::Feature>,
/// Whether to use fallback fonts.
fallback: bool,
/// The writing direction to use.
dir: Dir,
}

Expand Down Expand Up @@ -630,7 +645,7 @@ pub(super) fn shape<'a>(
};

if !text.is_empty() {
shape_segment(&mut ctx, base, text, families(styles));
shape_segment(&mut ctx, base, text, font_list_entries(styles));
}

track_and_space(&mut ctx);
Expand Down Expand Up @@ -660,7 +675,7 @@ fn shape_segment<'a>(
ctx: &mut ShapingContext,
base: usize,
text: &str,
mut families: impl Iterator<Item = &'a str> + Clone,
mut families: impl Iterator<Item = &'a FontListEntry> + Clone,
) {
// Fonts dont have newlines and tabs.
if text.chars().all(|c| c == '\n' || c == '\t') {
Expand All @@ -671,9 +686,10 @@ fn shape_segment<'a>(
let world = ctx.engine.world;
let book = world.book();
let mut selection = families.find_map(|family| {
book.select(family, ctx.variant)
book.select(family.family.as_str(), ctx.variant)
.and_then(|id| world.font(id))
.filter(|font| !ctx.used.contains(font))
.map(|d| (d, family.features()))
});

// Do font fallback if the families are exhausted and fallback is enabled.
Expand All @@ -682,11 +698,12 @@ fn shape_segment<'a>(
selection = book
.select_fallback(first, ctx.variant, text)
.and_then(|id| world.font(id))
.filter(|font| !ctx.used.contains(font));
.filter(|font| !ctx.used.contains(font))
.map(|d| (d, Vec::new()));
}

// Extract the font id or shape notdef glyphs if we couldn't find any font.
let Some(font) = selection else {
let Some((font, mut features)) = selection else {
if let Some(font) = ctx.used.first().cloned() {
shape_tofus(ctx, base, text, font);
}
Expand Down Expand Up @@ -714,12 +731,13 @@ fn shape_segment<'a>(
// Prepare the shape plan. This plan depends on direction, script, language,
// and features, but is independent from the text and can thus be
// memoized.
features.extend_from_slice(&ctx.features);
let plan = create_shape_plan(
&font,
buffer.direction(),
buffer.script(),
buffer.language().as_ref(),
&ctx.features,
&features,
);

// Shape!
Expand Down
9 changes: 6 additions & 3 deletions crates/typst/src/math/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use crate::model::ParElem;
use crate::realize::StyleVec;
use crate::syntax::{is_newline, Span};
use crate::text::{
features, BottomEdge, BottomEdgeMetric, Font, TextElem, TextSize, TopEdge,
TopEdgeMetric,
features, BottomEdge, BottomEdgeMetric, Font, FontListEntry, TextElem, TextSize,
TopEdge, TopEdgeMetric,
};

macro_rules! scaled {
Expand Down Expand Up @@ -71,6 +71,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
styles: StyleChain<'a>,
base: Size,
font: &'a Font,
fle: &FontListEntry,
) -> Self {
let math_table = font.ttf().tables().math.unwrap();
let gsub_table = font.ttf().tables().gsub;
Expand All @@ -89,7 +90,9 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
_ => None,
});

let features = features(styles);
let base_features = features(styles);
let mut features = fle.features();
features.extend_from_slice(&base_features);
let glyphwise_tables = gsub_table.map(|gsub| {
features
.into_iter()
Expand Down
27 changes: 14 additions & 13 deletions crates/typst/src/math/equation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use crate::math::{
use crate::model::{Numbering, Outlinable, ParElem, Refable, Supplement};
use crate::syntax::Span;
use crate::text::{
families, variant, Font, FontFamily, FontList, FontWeight, LocalName, TextElem,
font_list_entries, variant, Font, FontFamily, FontList, FontListEntry, FontWeight,
LocalName, TextElem,
};
use crate::utils::{NonZeroExt, Numeric};
use crate::World;
Expand Down Expand Up @@ -188,8 +189,8 @@ impl ShowSet for Packed<EquationElem> {
out.set(EquationElem::set_size(MathSize::Text));
}
out.set(TextElem::set_weight(FontWeight::from_number(450)));
out.set(TextElem::set_font(FontList(vec![FontFamily::new(
"New Computer Modern Math",
out.set(TextElem::set_font(FontList(vec![FontListEntry::from_family(
FontFamily::new("New Computer Modern Math"),
)])));
out
}
Expand Down Expand Up @@ -276,9 +277,9 @@ fn layout_equation_inline(
) -> SourceResult<Vec<InlineItem>> {
assert!(!elem.block(styles));

let font = find_math_font(engine, styles, elem.span())?;
let (font, fle) = find_math_font(engine, styles, elem.span())?;

let mut ctx = MathContext::new(engine, locator, styles, region, &font);
let mut ctx = MathContext::new(engine, locator, styles, region, &font, fle);
let run = ctx.layout_into_run(elem, styles)?;

let mut items = if run.row_count() == 1 {
Expand Down Expand Up @@ -323,11 +324,11 @@ fn layout_equation_block(
assert!(elem.block(styles));

let span = elem.span();
let font = find_math_font(engine, styles, span)?;
let (font, fle) = find_math_font(engine, styles, span)?;

let mut locator = locator.split();
let mut ctx =
MathContext::new(engine, locator.next(&()), styles, regions.base(), &font);
MathContext::new(engine, locator.next(&()), styles, regions.base(), &font, fle);
let full_equation_builder = ctx
.layout_into_run(elem, styles)?
.multiline_frame_builder(&ctx, styles);
Expand Down Expand Up @@ -433,18 +434,18 @@ fn layout_equation_block(
Ok(Fragment::frames(frames))
}

fn find_math_font(
fn find_math_font<'a>(
engine: &mut Engine<'_>,
styles: StyleChain,
styles: StyleChain<'a>,
span: Span,
) -> SourceResult<Font> {
) -> SourceResult<(Font, &'a FontListEntry)> {
let variant = variant(styles);
let world = engine.world;
let Some(font) = families(styles).find_map(|family| {
let id = world.book().select(family, variant)?;
let Some(font) = font_list_entries(styles).find_map(|family| {
let id = world.book().select(family.family.as_str(), variant)?;
let font = world.font(id)?;
let _ = font.ttf().tables().math?.constants?;
Some(font)
Some((font, family))
}) else {
bail!(span, "current font does not support math");
};
Expand Down