summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--macros/Cargo.toml13
-rw-r--r--macros/src/lib.rs171
-rw-r--r--src/eval/node.rs7
-rw-r--r--src/eval/styles.rs79
-rw-r--r--src/layout/mod.rs27
-rw-r--r--src/library/flow.rs5
-rw-r--r--src/library/link.rs7
-rw-r--r--src/library/mod.rs3
-rw-r--r--src/library/page.rs49
-rw-r--r--src/library/par.rs32
-rw-r--r--src/library/text.rs117
12 files changed, 333 insertions, 178 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8b77f22d..7667942e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,6 +29,7 @@ rustybuzz = "0.4"
serde = { version = "1", features = ["derive", "rc"] }
svg2pdf = { version = "0.1", default-features = false, features = ["text", "png", "jpeg"] }
ttf-parser = "0.12"
+typst-macros = { path = "./macros" }
unicode-bidi = "0.3.5"
unicode-segmentation = "1"
unicode-xid = "0.2"
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
new file mode 100644
index 00000000..6a7f5522
--- /dev/null
+++ b/macros/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "typst-macros"
+version = "0.1.0"
+authors = ["The Typst Project Developers"]
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1"
+quote = "1"
+syn = { version = "1", features = ["full"] }
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
new file mode 100644
index 00000000..3b1d0548
--- /dev/null
+++ b/macros/src/lib.rs
@@ -0,0 +1,171 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use syn::parse_quote;
+use syn::spanned::Spanned;
+use syn::{Error, Result};
+
+/// Generate node properties.
+#[proc_macro_attribute]
+pub fn properties(_: TokenStream, item: TokenStream) -> TokenStream {
+ let impl_block = syn::parse_macro_input!(item as syn::ItemImpl);
+ expand(impl_block).unwrap_or_else(|err| err.to_compile_error()).into()
+}
+
+/// Expand a property impl block for a node.
+fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
+ // Split the node type into name and generic type arguments.
+ let (self_name, self_args) = parse_self(&*impl_block.self_ty)?;
+
+ // Rewrite the const items from values to keys.
+ let mut style_ids = vec![];
+ let mut modules = vec![];
+ for item in &mut impl_block.items {
+ if let syn::ImplItem::Const(item) = item {
+ let (style_id, module) = process_const(item, &self_name, &self_args)?;
+ style_ids.push(style_id);
+ modules.push(module);
+ }
+ }
+
+ // Here, we use the collected `style_ids` to provide a function that checks
+ // whether a property belongs to the node.
+ impl_block.items.insert(0, parse_quote! {
+ /// Check whether the property with the given type id belongs to `Self`.
+ pub fn has_property(id: StyleId) -> bool {
+ [#(#style_ids),*].contains(&id)
+ }
+ });
+
+ // Put everything into a module with a hopefully unique type to isolate
+ // it from the outside.
+ let module = quote::format_ident!("{}_types", self_name);
+ Ok(quote! {
+ #[allow(non_snake_case)]
+ mod #module {
+ use std::marker::PhantomData;
+ use once_cell::sync::Lazy;
+ use crate::eval::{Property, StyleId};
+ use super::*;
+
+ #impl_block
+ #(#modules)*
+ }
+ })
+}
+
+/// Parse the name and generic type arguments of the node type.
+fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> {
+ // Extract the node type for which we want to generate properties.
+ let path = match self_ty {
+ syn::Type::Path(path) => path,
+ ty => return Err(Error::new(ty.span(), "must be a path type")),
+ };
+
+ // Split up the type into its name and its generic type arguments.
+ let last = path.path.segments.last().unwrap();
+ let self_name = last.ident.to_string();
+ let self_args = match &last.arguments {
+ syn::PathArguments::AngleBracketed(args) => args
+ .args
+ .iter()
+ .filter_map(|arg| match arg {
+ syn::GenericArgument::Type(ty) => Some(ty),
+ _ => None,
+ })
+ .collect(),
+ _ => vec![],
+ };
+
+ Ok((self_name, self_args))
+}
+
+/// Process a single const item.
+fn process_const(
+ item: &mut syn::ImplItemConst,
+ self_name: &str,
+ self_args: &[&syn::Type],
+) -> Result<(syn::Expr, syn::ItemMod)> {
+ // The type of the property's value is what the user of our macro wrote
+ // as type of the const ...
+ let value_ty = &item.ty;
+
+ // ... but the real type of the const becomes Key<#key_param>.
+ let key_param = if self_args.is_empty() {
+ quote! { #value_ty }
+ } else {
+ quote! { (#value_ty, #(#self_args),*) }
+ };
+
+ // The display name, e.g. `TextNode::STRONG`.
+ let name = format!("{}::{}", self_name, &item.ident);
+
+ // The default value of the property is what the user wrote as
+ // initialization value of the const.
+ let default = &item.expr;
+
+ // Look for a folding function like `#[fold(u64::add)]`.
+ let mut combinator = None;
+ for attr in &item.attrs {
+ if attr.path.is_ident("fold") {
+ let fold: syn::Expr = attr.parse_args()?;
+ combinator = Some(quote! {
+ fn combine(inner: Self::Value, outer: Self::Value) -> Self::Value {
+ let f: fn(Self::Value, Self::Value) -> Self::Value = #fold;
+ f(inner, outer)
+ }
+ });
+ }
+ }
+
+ // The implementation of the `Property` trait.
+ let property_impl = quote! {
+ impl<T: 'static> Property for Key<T> {
+ type Value = #value_ty;
+
+ const NAME: &'static str = #name;
+
+ fn default() -> Self::Value {
+ #default
+ }
+
+ fn default_ref() -> &'static Self::Value {
+ static LAZY: Lazy<#value_ty> = Lazy::new(|| #default);
+ &*LAZY
+ }
+
+ #combinator
+ }
+ };
+
+ // The module that will contain the `Key` type.
+ let module_name = &item.ident;
+
+ // Generate the style id and module code.
+ let style_id = parse_quote! { StyleId::of::<#module_name::Key<#key_param>>() };
+ let module = parse_quote! {
+ #[allow(non_snake_case)]
+ mod #module_name {
+ use super::*;
+
+ pub struct Key<T>(pub PhantomData<T>);
+ impl<T> Copy for Key<T> {}
+ impl<T> Clone for Key<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ #property_impl
+ }
+ };
+
+ // Replace type and initializer expression with the `Key`.
+ item.attrs.retain(|attr| !attr.path.is_ident("fold"));
+ item.ty = parse_quote! { #module_name::Key<#key_param> };
+ item.expr = parse_quote! { #module_name::Key(PhantomData) };
+
+ Ok((style_id, module))
+}
diff --git a/src/eval/node.rs b/src/eval/node.rs
index a04fe84b..acdf4ed6 100644
--- a/src/eval/node.rs
+++ b/src/eval/node.rs
@@ -1,6 +1,7 @@
use std::convert::TryFrom;
use std::fmt::Debug;
use std::hash::Hash;
+use std::iter::Sum;
use std::mem;
use std::ops::{Add, AddAssign};
@@ -127,6 +128,12 @@ impl AddAssign for Node {
}
}
+impl Sum for Node {
+ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
+ Self::Sequence(iter.map(|n| (n, Styles::new())).collect())
+ }
+}
+
/// Packs a [`Node`] into a flow or whole document.
struct Packer {
/// Whether this packer produces the top-level document.
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index 555c2a61..2396646f 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -68,7 +68,9 @@ impl Styles {
where
P::Value: Copy,
{
- self.get_direct(key).copied().unwrap_or_else(P::default)
+ self.get_direct(key)
+ .map(|&v| P::combine(v, P::default()))
+ .unwrap_or_else(P::default)
}
/// Get a reference to a style property.
@@ -249,9 +251,6 @@ pub trait Property: Copy + 'static {
/// The name of the property, used for debug printing.
const NAME: &'static str;
- /// Combine the property with an outer value.
- fn combine(inner: Self::Value, outer: Self::Value) -> Self::Value;
-
/// The default value of the property.
fn default() -> Self::Value;
@@ -261,6 +260,11 @@ pub trait Property: Copy + 'static {
/// `properties!` macro. This way, expensive defaults don't need to be
/// recreated all the time.
fn default_ref() -> &'static Self::Value;
+
+ /// Combine the property with an outer value.
+ fn combine(inner: Self::Value, _: Self::Value) -> Self::Value {
+ inner
+ }
}
/// A unique identifier for a style property.
@@ -274,73 +278,6 @@ impl StyleId {
}
}
-/// Generate the property keys for a node.
-macro_rules! properties {
- ($node:ty, $(
- $(#[doc = $doc:expr])*
- $(#[fold($combine:expr)])?
- $name:ident: $type:ty = $default:expr
- ),* $(,)?) => {
- // TODO(set): Fix possible name clash.
- mod properties {
- use std::marker::PhantomData;
- use $crate::eval::{Property, StyleId};
- use super::*;
-
- $(#[allow(non_snake_case)] mod $name {
- use once_cell::sync::Lazy;
- use super::*;
-
- pub struct Key<T>(pub PhantomData<T>);
-
- impl<T> Copy for Key<T> {}
- impl<T> Clone for Key<T> {
- fn clone(&self) -> Self {
- *self
- }
- }
-
- impl Property for Key<$type> {
- type Value = $type;
-
- const NAME: &'static str = concat!(
- stringify!($node), "::", stringify!($name)
- );
-
- #[allow(unused_mut, unused_variables)]
- fn combine(mut inner: Self::Value, outer: Self::Value) -> Self::Value {
- $(
- let combine: fn(Self::Value, Self::Value) -> Self::Value = $combine;
- inner = combine(inner, outer);
- )?
- inner
- }
-
- fn default() -> Self::Value {
- $default
- }
-
- fn default_ref() -> &'static Self::Value {
- static LAZY: Lazy<$type> = Lazy::new(|| $default);
- &*LAZY
- }
- }
- })*
-
- impl $node {
- /// Check whether the property with the given type id belongs to
- /// `Self`.
- pub fn has_property(id: StyleId) -> bool {
- false || $(id == StyleId::of::<$name::Key<$type>>())||*
- }
-
- $($(#[doc = $doc])* pub const $name: $name::Key<$type>
- = $name::Key(PhantomData);)*
- }
- }
- };
-}
-
/// Set a style property to a value if the value is `Some`.
macro_rules! set {
($styles:expr, $target:expr => $value:expr) => {
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 4ede39b2..cf714f88 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -18,7 +18,7 @@ use std::rc::Rc;
use crate::eval::Styles;
use crate::font::FontStore;
use crate::frame::Frame;
-use crate::geom::{Align, Linear, Point, Sides, Spec, Transform};
+use crate::geom::{Align, Linear, Point, Sides, Size, Spec, Transform};
use crate::image::ImageStore;
use crate::library::{AlignNode, DocumentNode, PadNode, SizedNode, TransformNode};
use crate::Context;
@@ -232,6 +232,12 @@ impl PackedNode {
}
}
+impl Default for PackedNode {
+ fn default() -> Self {
+ EmptyNode.pack()
+ }
+}
+
impl Debug for PackedNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() {
@@ -282,3 +288,22 @@ where
state.finish()
}
}
+
+/// A layout node that produces an empty frame.
+///
+/// The packed version of this is returned by [`PackedNode::default`].
+#[derive(Debug, Hash)]
+pub struct EmptyNode;
+
+impl Layout for EmptyNode {
+ fn layout(
+ &self,
+ _: &mut LayoutContext,
+ regions: &Regions,
+ ) -> Vec<Constrained<Rc<Frame>>> {
+ let size = regions.expand.select(regions.current, Size::zero());
+ let mut cts = Constraints::new(regions.expand);
+ cts.exact = regions.current.filter(regions.expand);
+ vec![Frame::new(size).constrain(cts)]
+ }
+}
diff --git a/src/library/flow.rs b/src/library/flow.rs
index de6ab812..cfa761b6 100644
--- a/src/library/flow.rs
+++ b/src/library/flow.rs
@@ -134,9 +134,8 @@ impl<'a> FlowLayouter<'a> {
match child {
FlowChild::Break(styles) => {
let chain = styles.chain(&ctx.styles);
- let amount = chain
- .get(ParNode::SPACING)
- .resolve(chain.get(TextNode::SIZE).abs);
+ let em = chain.get(TextNode::SIZE).abs;
+ let amount = chain.get(ParNode::SPACING).resolve(em);
self.layout_absolute(amount.into());
}
FlowChild::Spacing(node) => match node.kind {
diff --git a/src/library/link.rs b/src/library/link.rs
index 114d25a1..40604a62 100644
--- a/src/library/link.rs
+++ b/src/library/link.rs
@@ -21,9 +21,8 @@ pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
#[derive(Debug, Hash)]
pub struct LinkNode;
-properties! {
- LinkNode,
-
+#[properties]
+impl LinkNode {
/// An URL to link to.
- URL: Option<String> = None,
+ pub const URL: Option<String> = None;
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 1e2ee224..5852f2bb 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -26,6 +26,8 @@ mod prelude {
pub use std::fmt::{self, Debug, Formatter};
pub use std::rc::Rc;
+ pub use typst_macros::properties;
+
pub use crate::diag::{At, TypResult};
pub use crate::eval::{
Args, Construct, EvalContext, Node, Property, Set, Smart, Styles, Value,
@@ -80,6 +82,7 @@ pub fn new() -> Scope {
std.def_func("block", block);
std.def_func("pagebreak", pagebreak);
std.def_func("parbreak", parbreak);
+ std.def_func("linebreak", linebreak);
std.def_func("stack", stack);
std.def_func("grid", grid);
std.def_func("pad", pad);
diff --git a/src/library/page.rs b/src/library/page.rs
index 0b0cc2d9..3bb5cbd3 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -20,27 +20,26 @@ pub struct PageNode {
pub styles: Styles,
}
-properties! {
- PageNode,
-
+#[properties]
+impl PageNode {
/// The unflipped width of the page.
- WIDTH: Smart<Length> = Smart::Custom(Paper::default().width()),
+ pub const WIDTH: Smart<Length> = Smart::Custom(Paper::default().width());
/// The unflipped height of the page.
- HEIGHT: Smart<Length> = Smart::Custom(Paper::default().height()),
+ pub const HEIGHT: Smart<Length> = Smart::Custom(Paper::default().height());
/// The class of paper. Defines the default margins.
- CLASS: PaperClass = Paper::default().class(),
+ pub const CLASS: PaperClass = Paper::default().class();
/// Whether the page is flipped into landscape orientation.
- FLIPPED: bool = false,
+ pub const FLIPPED: bool = false;
/// The left margin.
- LEFT: Smart<Linear> = Smart::Auto,
+ pub const LEFT: Smart<Linear> = Smart::Auto;
/// The right margin.
- RIGHT: Smart<Linear> = Smart::Auto,
+ pub const RIGHT: Smart<Linear> = Smart::Auto;
/// The top margin.
- TOP: Smart<Linear> = Smart::Auto,
+ pub const TOP: Smart<Linear> = Smart::Auto;
/// The bottom margin.
- BOTTOM: Smart<Linear> = Smart::Auto,
+ pub const BOTTOM: Smart<Linear> = Smart::Auto;
/// The page's background color.
- FILL: Option<Paint> = None,
+ pub const FILL: Option<Paint> = None;
}
impl Construct for PageNode {
@@ -54,29 +53,29 @@ impl Construct for PageNode {
impl Set for PageNode {
fn set(styles: &mut Styles, args: &mut Args) -> TypResult<()> {
if let Some(paper) = args.named::<Paper>("paper")?.or_else(|| args.find()) {
- styles.set(PageNode::CLASS, paper.class());
- styles.set(PageNode::WIDTH, Smart::Custom(paper.width()));
- styles.set(PageNode::HEIGHT, Smart::Custom(paper.height()));
+ styles.set(Self::CLASS, paper.class());
+ styles.set(Self::WIDTH, Smart::Custom(paper.width()));
+ styles.set(Self::HEIGHT, Smart::Custom(paper.height()));
}
if let Some(width) = args.named("width")? {
- styles.set(PageNode::CLASS, PaperClass::Custom);
- styles.set(PageNode::WIDTH, width);
+ styles.set(Self::CLASS, PaperClass::Custom);
+ styles.set(Self::WIDTH, width);
}
if let Some(height) = args.named("height")? {
- styles.set(PageNode::CLASS, PaperClass::Custom);
- styles.set(PageNode::HEIGHT, height);
+ styles.set(Self::CLASS, PaperClass::Custom);
+ styles.set(Self::HEIGHT, height);
}
let margins = args.named("margins")?;
- set!(styles, PageNode::FLIPPED => args.named("flipped")?);
- set!(styles, PageNode::LEFT => args.named("left")?.or(margins));
- set!(styles, PageNode::TOP => args.named("top")?.or(margins));
- set!(styles, PageNode::RIGHT => args.named("right")?.or(margins));
- set!(styles, PageNode::BOTTOM => args.named("bottom")?.or(margins));
- set!(styles, PageNode::FILL => args.named("fill")?);
+ set!(styles, Self::FLIPPED => args.named("flipped")?);
+ set!(styles, Self::LEFT => args.named("left")?.or(margins));
+ set!(styles, Self::TOP => args.named("top")?.or(margins));
+ set!(styles, Self::RIGHT => args.named("right")?.or(margins));
+ set!(styles, Self::BOTTOM => args.named("bottom")?.or(margins));
+ set!(styles, Self::FILL => args.named("fill")?);
Ok(())
}
diff --git a/src/library/par.rs b/src/library/par.rs
index d63c2315..9a70b2c7 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -14,21 +14,25 @@ pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
Ok(Value::Node(Node::Parbreak))
}
+/// `linebreak`: Start a new line.
+pub fn linebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
+ Ok(Value::Node(Node::Linebreak))
+}
+
/// A node that arranges its children into a paragraph.
#[derive(Hash)]
pub struct ParNode(pub Vec<ParChild>);
-properties! {
- ParNode,
-
+#[properties]
+impl ParNode {
/// The direction for text and inline objects.
- DIR: Dir = Dir::LTR,
+ pub const DIR: Dir = Dir::LTR;
/// How to align text and inline objects in their line.
- ALIGN: Align = Align::Left,
+ pub const ALIGN: Align = Align::Left;
/// The spacing between lines (dependent on scaled font size).
- LEADING: Linear = Relative::new(0.65).into(),
+ pub const LEADING: Linear = Relative::new(0.65).into();
/// The spacing between paragraphs (dependent on scaled font size).
- SPACING: Linear = Relative::new(1.2).into(),
+ pub const SPACING: Linear = Relative::new(1.2).into();
}
impl Construct for ParNode {
@@ -70,10 +74,10 @@ impl Set for ParNode {
align = Some(if dir == Dir::LTR { Align::Left } else { Align::Right });
}
- set!(styles, ParNode::DIR => dir);
- set!(styles, ParNode::ALIGN => align);
- set!(styles, ParNode::LEADING => leading);
- set!(styles, ParNode::SPACING => spacing);
+ set!(styles, Self::DIR => dir);
+ set!(styles, Self::ALIGN => align);
+ set!(styles, Self::LEADING => leading);
+ set!(styles, Self::SPACING => spacing);
Ok(())
}
@@ -266,11 +270,9 @@ impl<'a> ParLayouter<'a> {
}
}
+ let em = ctx.styles.get(TextNode::SIZE).abs;
let align = ctx.styles.get(ParNode::ALIGN);
- let leading = ctx
- .styles
- .get(ParNode::LEADING)
- .resolve(ctx.styles.get(TextNode::SIZE).abs);
+ let leading = ctx.styles.get(ParNode::LEADING).resolve(em);
Self { align, leading, bidi, items, ranges }
}
diff --git a/src/library/text.rs b/src/library/text.rs
index 4baf4bc5..e0cbb1ad 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -51,77 +51,76 @@ pub struct TextNode {
pub styles: Styles,
}
-properties! {
- TextNode,
-
+#[properties]
+impl TextNode {
/// A prioritized sequence of font families.
- FAMILY_LIST: Vec<FontFamily> = vec![FontFamily::SansSerif],
+ pub const FAMILY_LIST: Vec<FontFamily> = vec![FontFamily::SansSerif];
/// The serif font family/families.
- SERIF_LIST: Vec<String> = vec!["ibm plex serif".into()],
+ pub const SERIF_LIST: Vec<String> = vec!["ibm plex serif".into()];
/// The sans-serif font family/families.
- SANS_SERIF_LIST: Vec<String> = vec!["ibm plex sans".into()],
+ pub const SANS_SERIF_LIST: Vec<String> = vec!["ibm plex sans".into()];
/// The monospace font family/families.
- MONOSPACE_LIST: Vec<String> = vec!["ibm plex mono".into()],
+ pub const MONOSPACE_LIST: Vec<String> = vec!["ibm plex mono".into()];
/// Whether to allow font fallback when the primary font list contains no
/// match.
- FALLBACK: bool = true,
+ pub const FALLBACK: bool = true;
/// How the font is styled.
- STYLE: FontStyle = FontStyle::Normal,
+ pub const STYLE: FontStyle = FontStyle::Normal;
/// The boldness / thickness of the font's glyphs.
- WEIGHT: FontWeight = FontWeight::REGULAR,
+ pub const WEIGHT: FontWeight = FontWeight::REGULAR;
/// The width of the glyphs.
- STRETCH: FontStretch = FontStretch::NORMAL,
+ pub const STRETCH: FontStretch = FontStretch::NORMAL;
/// Whether the font weight should be increased by 300.
#[fold(bool::bitxor)]
- STRONG: bool = false,
+ pub const STRONG: bool = false;
/// Whether the the font style should be inverted.
#[fold(bool::bitxor)]
- EMPH: bool = false,
+ pub const EMPH: bool = false;
/// Whether a monospace font should be preferred.
- MONOSPACE: bool = false,
+ pub const MONOSPACE: bool = false;
/// The glyph fill color.
- FILL: Paint = RgbaColor::BLACK.into(),
+ pub const FILL: Paint = RgbaColor::BLACK.into();
/// Decorative lines.
#[fold(|a, b| a.into_iter().chain(b).collect())]
- LINES: Vec<LineDecoration> = vec![],
+ pub const LINES: Vec<LineDecoration> = vec![];
/// The size of the glyphs.
#[fold(Linear::compose)]
- SIZE: Linear = Length::pt(11.0).into(),
+ pub const SIZE: Linear = Length::pt(11.0).into();
/// The amount of space that should be added between characters.
- TRACKING: Em = Em::zero(),
+ pub const TRACKING: Em = Em::zero();
/// The top end of the text bounding box.
- TOP_EDGE: VerticalFontMetric = VerticalFontMetric::CapHeight,
+ pub const TOP_EDGE: VerticalFontMetric = VerticalFontMetric::CapHeight;
/// The bottom end of the text bounding box.
- BOTTOM_EDGE: VerticalFontMetric = VerticalFontMetric::Baseline,
+ pub const BOTTOM_EDGE: VerticalFontMetric = VerticalFontMetric::Baseline;
/// Whether to apply kerning ("kern").
- KERNING: bool = true,
+ pub const KERNING: bool = true;
/// Whether small capital glyphs should be used. ("smcp")
- SMALLCAPS: bool = false,
+ pub const SMALLCAPS: bool = false;
/// Whether to apply stylistic alternates. ("salt")
- ALTERNATES: bool = false,
+ pub const ALTERNATES: bool = false;
/// Which stylistic set to apply. ("ss01" - "ss20")
- STYLISTIC_SET: Option<StylisticSet> = None,
+ pub const STYLISTIC_SET: Option<StylisticSet> = None;
/// Whether standard ligatures are active. ("liga", "clig")
- LIGATURES: bool = true,
+ pub const LIGATURES: bool = true;
/// Whether ligatures that should be used sparingly are active. ("dlig")
- DISCRETIONARY_LIGATURES: bool = false,
+ pub const DISCRETIONARY_LIGATURES: bool = false;
/// Whether historical ligatures are active. ("hlig")
- HISTORICAL_LIGATURES: bool = false,
+ pub const HISTORICAL_LIGATURES: bool = false;
/// Which kind of numbers / figures to select.
- NUMBER_TYPE: Smart<NumberType> = Smart::Auto,
+ pub const NUMBER_TYPE: Smart<NumberType> = Smart::Auto;
/// The width of numbers / figures.
- NUMBER_WIDTH: Smart<NumberWidth> = Smart::Auto,
+ pub const NUMBER_WIDTH: Smart<NumberWidth> = Smart::Auto;
/// How to position numbers.
- NUMBER_POSITION: NumberPosition = NumberPosition::Normal,
+ pub const NUMBER_POSITION: NumberPosition = NumberPosition::Normal;
/// Whether to have a slash through the zero glyph. ("zero")
- SLASHED_ZERO: bool = false,
+ pub const SLASHED_ZERO: bool = false;
/// Whether to convert fractions. ("frac")
- FRACTIONS: bool = false,
+ pub const FRACTIONS: bool = false;
/// Raw OpenType features to apply.
- FEATURES: Vec<(Tag, u32)> = vec![],
+ pub const FEATURES: Vec<(Tag, u32)> = vec![];
}
impl Construct for TextNode {
@@ -140,32 +139,32 @@ impl Set for TextNode {
(!families.is_empty()).then(|| families)
});
- set!(styles, TextNode::FAMILY_LIST => list);
- set!(styles, TextNode::SERIF_LIST => args.named("serif")?);
- set!(styles, TextNode::SANS_SERIF_LIST => args.named("sans-serif")?);
- set!(styles, TextNode::MONOSPACE_LIST => args.named("monospace")?);
- set!(styles, TextNode::FALLBACK => args.named("fallback")?);
- set!(styles, TextNode::STYLE => args.named("style")?);
- set!(styles, TextNode::WEIGHT => args.named("weight")?);
- set!(styles, TextNode::STRETCH => args.named("stretch")?);
- set!(styles, TextNode::FILL => args.named("fill")?.or_else(|| args.find()));
- set!(styles, TextNode::SIZE => args.named("size")?.or_else(|| args.find()));
- set!(styles, TextNode::TRACKING => args.named("tracking")?.map(Em::new));
- set!(styles, TextNode::TOP_EDGE => args.named("top-edge")?);
- set!(styles, TextNode::BOTTOM_EDGE => args.named("bottom-edge")?);
- set!(styles, TextNode::KERNING => args.named("kerning")?);
- set!(styles, TextNode::SMALLCAPS => args.named("smallcaps")?);
- set!(styles, TextNode::ALTERNATES => args.named("alternates")?);
- set!(styles, TextNode::STYLISTIC_SET => args.named("stylistic-set")?);
- set!(styles, TextNode::LIGATURES => args.named("ligatures")?);
- set!(styles, TextNode::DISCRETIONARY_LIGATURES => args.named("discretionary-ligatures")?);
- set!(styles, TextNode::HISTORICAL_LIGATURES => args.named("historical-ligatures")?);
- set!(styles, TextNode::NUMBER_TYPE => args.named("number-type")?);
- set!(styles, TextNode::NUMBER_WIDTH => args.named("number-width")?);
- set!(styles, TextNode::NUMBER_POSITION => args.named("number-position")?);
- set!(styles, TextNode::SLASHED_ZERO => args.named("slashed-zero")?);
- set!(styles, TextNode::FRACTIONS => args.named("fractions")?);
- set!(styles, TextNode::FEATURES => args.named("features")?);
+ set!(styles, Self::FAMILY_LIST => list);
+ set!(styles, Self::SERIF_LIST => args.named("serif")?);
+ set!(styles, Self::SANS_SERIF_LIST => args.named("sans-serif")?);
+ set!(styles, Self::MONOSPACE_LIST => args.named("monospace")?);
+ set!(styles, Self::FALLBACK => args.named("fallback")?);
+ set!(styles, Self::STYLE => args.named("style")?);
+ set!(styles, Self::WEIGHT => args.named("weight")?);
+ set!(styles, Self::STRETCH => args.named("stretch")?);
+ set!(styles, Self::FILL => args.named("fill")?.or_else(|| args.find()));
+ set!(styles, Self::SIZE => args.named("size")?.or_else(|| args.find()));
+ set!(styles, Self::TRACKING => args.named("tracking")?.map(Em::new));
+ set!(styles, Self::TOP_EDGE => args.named("top-edge")?);
+ set!(styles, Self::BOTTOM_EDGE => args.named("bottom-edge")?);
+ set!(styles, Self::KERNING => args.named("kerning")?);
+ set!(styles, Self::SMALLCAPS => args.named("smallcaps")?);
+ set!(styles, Self::ALTERNATES => args.named("alternates")?);
+ set!(styles, Self::STYLISTIC_SET => args.named("stylistic-set")?);
+ set!(styles, Self::LIGATURES => args.named("ligatures")?);
+ set!(styles, Self::DISCRETIONARY_LIGATURES => args.named("discretionary-ligatures")?);
+ set!(styles, Self::HISTORICAL_LIGATURES => args.named("historical-ligatures")?);
+ set!(styles, Self::NUMBER_TYPE => args.named("number-type")?);
+ set!(styles, Self::NUMBER_WIDTH => args.named("number-width")?);
+ set!(styles, Self::NUMBER_POSITION => args.named("number-position")?);
+ set!(styles, Self::SLASHED_ZERO => args.named("slashed-zero")?);
+ set!(styles, Self::FRACTIONS => args.named("fractions")?);
+ set!(styles, Self::FEATURES => args.named("features")?);
Ok(())
}