This commit is contained in:
Laurenz 2021-11-14 14:53:01 +01:00
parent 14048937b8
commit 8a38899c98
6 changed files with 57 additions and 61 deletions

View File

@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize};
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
/// Early-return with a vec-boxed [`Error`]. /// Early-return with a vec-boxed [`Error`].
#[macro_export]
macro_rules! bail { macro_rules! bail {
($span:expr, $message:expr $(,)?) => { ($span:expr, $message:expr $(,)?) => {
return Err($crate::diag::Error::boxed($span, $message,)) return Err($crate::diag::Error::boxed($span, $message,))

View File

@ -72,14 +72,6 @@ impl Value {
} }
} }
/// Check whether the value is castable into a specific type.
pub fn is<T>(&self) -> bool
where
T: Cast<Value>,
{
T::is(self)
}
/// Try to cast the value into a specific type. /// Try to cast the value into a specific type.
pub fn cast<T>(self) -> StrResult<T> pub fn cast<T>(self) -> StrResult<T>
where where

View File

@ -149,31 +149,48 @@ impl FontStore {
pub fn families(&self) -> impl Iterator<Item = &str> + '_ { pub fn families(&self) -> impl Iterator<Item = &str> + '_ {
// Since the keys are lowercased, we instead use the family field of the // Since the keys are lowercased, we instead use the family field of the
// first face's info. // first face's info.
let faces = self.loader.faces();
self.families self.families
.values() .values()
.map(move |id| self.loader.faces()[id[0].0 as usize].family.as_str()) .map(move |id| faces[id[0].0 as usize].family.as_str())
} }
} }
/// A font face. /// A font face.
pub struct Face { pub struct Face {
/// The raw face data, possibly shared with other faces from the same
/// collection. Must stay alive put, because `ttf` points into it using
/// unsafe code.
buffer: Rc<Vec<u8>>, buffer: Rc<Vec<u8>>,
/// The face's index in the collection (zero if not a collection).
index: u32, index: u32,
/// The underlying ttf-parser/rustybuzz face.
ttf: rustybuzz::Face<'static>, ttf: rustybuzz::Face<'static>,
units_per_em: f64, /// How many font units represent one em unit.
pub units_per_em: f64,
/// The distance from the baseline to the typographic ascender.
pub ascender: Em, pub ascender: Em,
/// The approximate height of uppercase letters.
pub cap_height: Em, pub cap_height: Em,
/// The approximate height of non-ascending lowercase letters.
pub x_height: Em, pub x_height: Em,
/// The distance from the baseline to the typographic descender.
pub descender: Em, pub descender: Em,
/// Recommended metrics for a strikethrough line.
pub strikethrough: LineMetrics, pub strikethrough: LineMetrics,
/// Recommended metrics for an underline.
pub underline: LineMetrics, pub underline: LineMetrics,
/// Recommended metrics for an overline.
pub overline: LineMetrics, pub overline: LineMetrics,
} }
/// Metrics for a decorative line. /// Metrics for a decorative line.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct LineMetrics { pub struct LineMetrics {
/// The thickness of the line.
pub strength: Em, pub strength: Em,
/// The vertical offset of the line from the baseline. Positive goes
/// upwards, negative downwards.
pub position: Em, pub position: Em,
} }
@ -190,7 +207,6 @@ impl Face {
unsafe { std::slice::from_raw_parts(buffer.as_ptr(), buffer.len()) }; unsafe { std::slice::from_raw_parts(buffer.as_ptr(), buffer.len()) };
let ttf = rustybuzz::Face::from_slice(slice, index)?; let ttf = rustybuzz::Face::from_slice(slice, index)?;
let units_per_em = f64::from(ttf.units_per_em()); let units_per_em = f64::from(ttf.units_per_em());
let to_em = |units| Em::from_units(units, units_per_em); let to_em = |units| Em::from_units(units, units_per_em);
@ -252,11 +268,6 @@ impl Face {
&self.ttf &self.ttf
} }
/// Get the number of units per em.
pub fn units_per_em(&self) -> f64 {
self.units_per_em
}
/// Convert from font units to an em length. /// Convert from font units to an em length.
pub fn to_em(&self, units: impl Into<f64>) -> Em { pub fn to_em(&self, units: impl Into<f64>) -> Em {
Em::from_units(units, self.units_per_em) Em::from_units(units, self.units_per_em)

View File

@ -2,11 +2,12 @@ use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use std::ops::Range; use std::ops::Range;
use rustybuzz::{Feature, Tag, UnicodeBuffer}; use rustybuzz::{Feature, UnicodeBuffer};
use ttf_parser::Tag;
use super::prelude::*; use super::prelude::*;
use crate::font::{ use crate::font::{
Face, FaceId, FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, Face, FaceId, FontFamily, FontStore, FontStretch, FontStyle, FontVariant, FontWeight,
VerticalFontMetric, VerticalFontMetric,
}; };
use crate::geom::{Dir, Em, Length, Point, Size}; use crate::geom::{Dir, Em, Length, Point, Size};
@ -19,7 +20,7 @@ use crate::util::SliceExt;
pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
struct FontDef(Rc<Vec<FontFamily>>); struct FontDef(Rc<Vec<FontFamily>>);
struct FamilyDef(Rc<Vec<String>>); struct FamilyDef(Rc<Vec<String>>);
struct FeatureList(Vec<(String, u32)>); struct FeatureList(Vec<(Tag, u32)>);
struct StylisticSet(Option<u8>); struct StylisticSet(Option<u8>);
castable! { castable! {
@ -134,21 +135,23 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
castable! { castable! {
FeatureList: "array of strings or dictionary mapping tags to integers", FeatureList: "array of strings or dictionary mapping tags to integers",
Value::Array(values) => Self(values Value::Array(values) => Self(
values
.into_iter() .into_iter()
.filter_map(|v| v.cast().ok()) .filter_map(|v| v.cast().ok())
.map(|string: Str| (string.to_lowercase(), 1)) .map(|string: Str| (Tag::from_bytes_lossy(string.as_bytes()), 1))
.collect()), .collect()
Value::Dict(values) => Self(values ),
Value::Dict(values) => Self(
values
.into_iter() .into_iter()
.filter_map(|(k, v)| { .filter_map(|(k, v)| {
if let Ok(value) = v.cast::<i64>() { let tag = Tag::from_bytes_lossy(k.as_bytes());
Some((k.to_lowercase(), value as u32)) let num = v.cast::<i64>().ok()?.try_into().ok()?;
} else { Some((tag, num))
None
}
}) })
.collect()), .collect()
),
} }
let list = args.named("family")?.or_else(|| { let list = args.named("family")?.or_else(|| {
@ -266,11 +269,10 @@ pub fn shape<'a>(
let mut glyphs = vec![]; let mut glyphs = vec![];
if !text.is_empty() { if !text.is_empty() {
shape_segment( shape_segment(
ctx, ctx.fonts,
&mut glyphs, &mut glyphs,
0, 0,
text, text,
style.size,
style.variant(), style.variant(),
style.families(), style.families(),
None, None,
@ -452,11 +454,10 @@ enum Side {
/// Shape text with font fallback using the `families` iterator. /// Shape text with font fallback using the `families` iterator.
fn shape_segment<'a>( fn shape_segment<'a>(
ctx: &mut LayoutContext, fonts: &mut FontStore,
glyphs: &mut Vec<ShapedGlyph>, glyphs: &mut Vec<ShapedGlyph>,
base: usize, base: usize,
text: &str, text: &str,
size: Length,
variant: FontVariant, variant: FontVariant,
mut families: impl Iterator<Item = &'a str> + Clone, mut families: impl Iterator<Item = &'a str> + Clone,
mut first_face: Option<FaceId>, mut first_face: Option<FaceId>,
@ -468,7 +469,7 @@ fn shape_segment<'a>(
// Try to load the next available font family. // Try to load the next available font family.
match families.next() { match families.next() {
Some(family) => { Some(family) => {
if let Some(id) = ctx.fonts.select(family, variant) { if let Some(id) = fonts.select(family, variant) {
break (id, true); break (id, true);
} }
} }
@ -495,7 +496,7 @@ fn shape_segment<'a>(
}); });
// Shape! // Shape!
let mut face = ctx.fonts.get(face_id); let mut face = fonts.get(face_id);
let buffer = rustybuzz::shape(face.ttf(), tags, buffer); let buffer = rustybuzz::shape(face.ttf(), tags, buffer);
let infos = buffer.glyph_infos(); let infos = buffer.glyph_infos();
let pos = buffer.glyph_positions(); let pos = buffer.glyph_positions();
@ -545,11 +546,9 @@ fn shape_segment<'a>(
// Glyphs: E C _ _ A // Glyphs: E C _ _ A
// Clusters: 8 6 4 2 0 // Clusters: 8 6 4 2 0
// k=2 i=3 // k=2 i=3
let ltr = dir.is_positive(); let ltr = dir.is_positive();
let first = if ltr { k } else { i }; let first = if ltr { k } else { i };
let start = infos[first].cluster as usize; let start = infos[first].cluster as usize;
let last = if ltr { i.checked_add(1) } else { k.checked_sub(1) }; let last = if ltr { i.checked_add(1) } else { k.checked_sub(1) };
let end = last let end = last
.and_then(|last| infos.get(last)) .and_then(|last| infos.get(last))
@ -560,11 +559,10 @@ fn shape_segment<'a>(
// Recursively shape the tofu sequence with the next family. // Recursively shape the tofu sequence with the next family.
shape_segment( shape_segment(
ctx, fonts,
glyphs, glyphs,
base + range.start, base + range.start,
&text[range], &text[range],
size,
variant, variant,
families.clone(), families.clone(),
first_face, first_face,
@ -572,7 +570,7 @@ fn shape_segment<'a>(
tags, tags,
); );
face = ctx.fonts.get(face_id); face = fonts.get(face_id);
} }
i += 1; i += 1;
@ -687,12 +685,8 @@ fn tags(features: &FontFeatures) -> Vec<Feature> {
feat(b"frac", 1); feat(b"frac", 1);
} }
for (tag, value) in features.raw.iter() { for &(tag, value) in features.raw.iter() {
tags.push(Feature::new( tags.push(Feature::new(tag, value, ..))
Tag::from_bytes_lossy(tag.as_bytes()),
*value,
..,
))
} }
tags tags

View File

@ -144,6 +144,11 @@ impl SourceFile {
} }
} }
/// Create a source file without a real id and path, usually for testing.
pub fn detached(src: impl Into<String>) -> Self {
Self::new(SourceId(0), Path::new(""), src.into())
}
/// The file's abstract syntax tree. /// The file's abstract syntax tree.
pub fn ast(&self) -> TypResult<Markup> { pub fn ast(&self) -> TypResult<Markup> {
let red = RedNode::from_root(self.root.clone(), self.id); let red = RedNode::from_root(self.root.clone(), self.id);
@ -155,11 +160,6 @@ impl SourceFile {
} }
} }
/// Create a source file without a real id and path, usually for testing.
pub fn detached(src: impl Into<String>) -> Self {
Self::new(SourceId(0), Path::new(""), src.into())
}
/// The id of the source file. /// The id of the source file.
pub fn id(&self) -> SourceId { pub fn id(&self) -> SourceId {
self.id self.id

View File

@ -6,9 +6,9 @@ pub use paper::*;
use std::rc::Rc; use std::rc::Rc;
use crate::font::{ use ttf_parser::Tag;
FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric,
}; use crate::font::*;
use crate::geom::*; use crate::geom::*;
/// Defines a set of properties a template can be instantiated with. /// Defines a set of properties a template can be instantiated with.
@ -275,7 +275,7 @@ pub struct FontFeatures {
/// Configuration of numbers features. /// Configuration of numbers features.
pub numbers: NumberFeatures, pub numbers: NumberFeatures,
/// Raw OpenType features to apply. /// Raw OpenType features to apply.
pub raw: Vec<(String, u32)>, pub raw: Vec<(Tag, u32)>,
} }
impl Default for FontFeatures { impl Default for FontFeatures {