mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Cast content from string
This commit is contained in:
parent
49b8574b8d
commit
bfaf5447a7
@ -53,8 +53,7 @@ fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenSt
|
|||||||
let construct =
|
let construct =
|
||||||
construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?;
|
construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?;
|
||||||
|
|
||||||
let set = set.unwrap_or_else(|| generate_set(&properties));
|
let set = generate_set(&properties, set);
|
||||||
|
|
||||||
let showable = match stream.to_string().as_str() {
|
let showable = match stream.to_string().as_str() {
|
||||||
"" => false,
|
"" => false,
|
||||||
"showable" => true,
|
"showable" => true,
|
||||||
@ -244,10 +243,9 @@ fn process_const(
|
|||||||
/// A style property.
|
/// A style property.
|
||||||
struct Property {
|
struct Property {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
hidden: bool,
|
skip: bool,
|
||||||
referenced: bool,
|
referenced: bool,
|
||||||
shorthand: Option<Shorthand>,
|
shorthand: Option<Shorthand>,
|
||||||
variadic: bool,
|
|
||||||
resolve: bool,
|
resolve: bool,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
}
|
}
|
||||||
@ -261,10 +259,9 @@ enum Shorthand {
|
|||||||
fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
||||||
let mut property = Property {
|
let mut property = Property {
|
||||||
name: item.ident.clone(),
|
name: item.ident.clone(),
|
||||||
hidden: false,
|
skip: false,
|
||||||
referenced: false,
|
|
||||||
shorthand: None,
|
shorthand: None,
|
||||||
variadic: false,
|
referenced: false,
|
||||||
resolve: false,
|
resolve: false,
|
||||||
fold: false,
|
fold: false,
|
||||||
};
|
};
|
||||||
@ -279,7 +276,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
while let Some(token) = stream.next() {
|
while let Some(token) = stream.next() {
|
||||||
match token {
|
match token {
|
||||||
TokenTree::Ident(ident) => match ident.to_string().as_str() {
|
TokenTree::Ident(ident) => match ident.to_string().as_str() {
|
||||||
"hidden" => property.hidden = true,
|
"skip" => property.skip = true,
|
||||||
"shorthand" => {
|
"shorthand" => {
|
||||||
let short = if let Some(TokenTree::Group(group)) = stream.peek() {
|
let short = if let Some(TokenTree::Group(group)) = stream.peek() {
|
||||||
let span = group.span();
|
let span = group.span();
|
||||||
@ -296,7 +293,6 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
property.shorthand = Some(short);
|
property.shorthand = Some(short);
|
||||||
}
|
}
|
||||||
"referenced" => property.referenced = true,
|
"referenced" => property.referenced = true,
|
||||||
"variadic" => property.variadic = true,
|
|
||||||
"resolve" => property.resolve = true,
|
"resolve" => property.resolve = true,
|
||||||
"fold" => property.fold = true,
|
"fold" => property.fold = true,
|
||||||
_ => return Err(Error::new(ident.span(), "invalid attribute")),
|
_ => return Err(Error::new(ident.span(), "invalid attribute")),
|
||||||
@ -308,10 +304,10 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let span = property.name.span();
|
let span = property.name.span();
|
||||||
if property.shorthand.is_some() && property.variadic {
|
if property.skip && property.shorthand.is_some() {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
span,
|
span,
|
||||||
"shorthand and variadic are mutually exclusive",
|
"skip and shorthand are mutually exclusive",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,26 +322,24 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Auto-generate a `set` function from properties.
|
/// Auto-generate a `set` function from properties.
|
||||||
fn generate_set(properties: &[Property]) -> syn::ImplItemMethod {
|
fn generate_set(
|
||||||
|
properties: &[Property],
|
||||||
|
user: Option<syn::ImplItemMethod>,
|
||||||
|
) -> syn::ImplItemMethod {
|
||||||
|
let user = user.map(|method| {
|
||||||
|
let block = &method.block;
|
||||||
|
quote! { (|| -> TypResult<()> { #block; Ok(()) } )()?; }
|
||||||
|
});
|
||||||
|
|
||||||
let mut shorthands = vec![];
|
let mut shorthands = vec![];
|
||||||
let sets: Vec<_> = properties
|
let sets: Vec<_> = properties
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| !p.hidden)
|
.filter(|p| !p.skip)
|
||||||
.map(|property| {
|
.map(|property| {
|
||||||
let name = &property.name;
|
let name = &property.name;
|
||||||
let string = name.to_string().replace("_", "-").to_lowercase();
|
let string = name.to_string().replace("_", "-").to_lowercase();
|
||||||
|
|
||||||
let value = if property.variadic {
|
let value = if let Some(short) = &property.shorthand {
|
||||||
quote! {
|
|
||||||
match args.named(#string)? {
|
|
||||||
Some(value) => value,
|
|
||||||
None => {
|
|
||||||
let list: Vec<_> = args.all()?;
|
|
||||||
(!list.is_empty()).then(|| list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(short) = &property.shorthand {
|
|
||||||
match short {
|
match short {
|
||||||
Shorthand::Positional => quote! { args.named_or_find(#string)? },
|
Shorthand::Positional => quote! { args.named_or_find(#string)? },
|
||||||
Shorthand::Named(named) => {
|
Shorthand::Named(named) => {
|
||||||
@ -357,7 +351,6 @@ fn generate_set(properties: &[Property]) -> syn::ImplItemMethod {
|
|||||||
quote! { args.named(#string)? }
|
quote! { args.named(#string)? }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
quote! { styles.set_opt(Self::#name, #value); }
|
quote! { styles.set_opt(Self::#name, #value); }
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -371,8 +364,9 @@ fn generate_set(properties: &[Property]) -> syn::ImplItemMethod {
|
|||||||
});
|
});
|
||||||
|
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
fn set(args: &mut Args, constructor: bool) -> TypResult<StyleMap> {
|
||||||
let mut styles = StyleMap::new();
|
let mut styles = StyleMap::new();
|
||||||
|
#user
|
||||||
#(#bindings)*
|
#(#bindings)*
|
||||||
#(#sets)*
|
#(#sets)*
|
||||||
Ok(styles)
|
Ok(styles)
|
||||||
|
@ -44,20 +44,6 @@ impl Args {
|
|||||||
Self { span, items }
|
Self { span, items }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume and cast the first positional argument.
|
|
||||||
///
|
|
||||||
/// Returns a `missing argument: {what}` error if no positional argument is
|
|
||||||
/// left.
|
|
||||||
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
|
||||||
where
|
|
||||||
T: Cast<Spanned<Value>>,
|
|
||||||
{
|
|
||||||
match self.eat()? {
|
|
||||||
Some(v) => Ok(v),
|
|
||||||
None => bail!(self.span, "missing argument: {}", what),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume and cast the first positional argument if there is one.
|
/// Consume and cast the first positional argument if there is one.
|
||||||
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
|
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
|
||||||
where
|
where
|
||||||
@ -73,6 +59,20 @@ impl Args {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consume and cast the first positional argument.
|
||||||
|
///
|
||||||
|
/// Returns a `missing argument: {what}` error if no positional argument is
|
||||||
|
/// left.
|
||||||
|
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
||||||
|
where
|
||||||
|
T: Cast<Spanned<Value>>,
|
||||||
|
{
|
||||||
|
match self.eat()? {
|
||||||
|
Some(v) => Ok(v),
|
||||||
|
None => bail!(self.span, "missing argument: {}", what),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Find and consume the first castable positional argument.
|
/// Find and consume the first castable positional argument.
|
||||||
pub fn find<T>(&mut self) -> TypResult<Option<T>>
|
pub fn find<T>(&mut self) -> TypResult<Option<T>>
|
||||||
where
|
where
|
||||||
|
@ -43,11 +43,11 @@ impl Func {
|
|||||||
Self(Arc::new(Repr::Native(Native {
|
Self(Arc::new(Repr::Native(Native {
|
||||||
name,
|
name,
|
||||||
func: |ctx, args| {
|
func: |ctx, args| {
|
||||||
let styles = T::set(args)?;
|
let styles = T::set(args, true)?;
|
||||||
let content = T::construct(ctx, args)?;
|
let content = T::construct(ctx, args)?;
|
||||||
Ok(Value::Content(content.styled_with_map(styles.scoped())))
|
Ok(Value::Content(content.styled_with_map(styles.scoped())))
|
||||||
},
|
},
|
||||||
set: Some(T::set),
|
set: Some(|args| T::set(args, false)),
|
||||||
node: T::SHOWABLE.then(|| NodeId::of::<T>()),
|
node: T::SHOWABLE.then(|| NodeId::of::<T>()),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -165,7 +165,10 @@ pub trait Node: 'static {
|
|||||||
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
|
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
|
||||||
|
|
||||||
/// Parse the arguments into style properties for this node.
|
/// Parse the arguments into style properties for this node.
|
||||||
fn set(args: &mut Args) -> TypResult<StyleMap>;
|
///
|
||||||
|
/// When `constructor` is true, [`construct`](Self::construct) will run
|
||||||
|
/// after this invocation of `set`.
|
||||||
|
fn set(args: &mut Args, constructor: bool) -> TypResult<StyleMap>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A user-defined closure.
|
/// A user-defined closure.
|
||||||
|
@ -464,11 +464,19 @@ primitive! { f64: "float", Float, Int(v) => v as f64 }
|
|||||||
primitive! { RawLength: "length", Length }
|
primitive! { RawLength: "length", Length }
|
||||||
primitive! { Angle: "angle", Angle }
|
primitive! { Angle: "angle", Angle }
|
||||||
primitive! { Ratio: "ratio", Ratio }
|
primitive! { Ratio: "ratio", Ratio }
|
||||||
primitive! { Relative<RawLength>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() }
|
primitive! { Relative<RawLength>: "relative length",
|
||||||
|
Relative,
|
||||||
|
Length(v) => v.into(),
|
||||||
|
Ratio(v) => v.into()
|
||||||
|
}
|
||||||
primitive! { Fraction: "fraction", Fraction }
|
primitive! { Fraction: "fraction", Fraction }
|
||||||
primitive! { Color: "color", Color }
|
primitive! { Color: "color", Color }
|
||||||
primitive! { EcoString: "string", Str }
|
primitive! { EcoString: "string", Str }
|
||||||
primitive! { Content: "content", Content, None => Content::new() }
|
primitive! { Content: "content",
|
||||||
|
Content,
|
||||||
|
None => Content::new(),
|
||||||
|
Str(text) => Content::Text(text)
|
||||||
|
}
|
||||||
primitive! { Array: "array", Array }
|
primitive! { Array: "array", Array }
|
||||||
primitive! { Dict: "dictionary", Dict }
|
primitive! { Dict: "dictionary", Dict }
|
||||||
primitive! { Func: "function", Func }
|
primitive! { Func: "function", Func }
|
||||||
|
@ -24,19 +24,17 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
/// How to fill the shape.
|
/// How to fill the shape.
|
||||||
pub const FILL: Option<Paint> = None;
|
pub const FILL: Option<Paint> = None;
|
||||||
/// How to stroke the shape.
|
/// How to stroke the shape.
|
||||||
#[property(resolve, fold)]
|
#[property(skip, resolve, fold)]
|
||||||
pub const STROKE: Smart<Sides<Option<RawStroke>>> = Smart::Auto;
|
pub const STROKE: Smart<Sides<Option<RawStroke>>> = Smart::Auto;
|
||||||
|
|
||||||
/// How much to pad the shape's content.
|
/// How much to pad the shape's content.
|
||||||
#[property(resolve, fold)]
|
#[property(resolve, fold)]
|
||||||
pub const INSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
pub const INSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
||||||
|
|
||||||
/// How much to extend the shape's dimensions beyond the allocated space.
|
/// How much to extend the shape's dimensions beyond the allocated space.
|
||||||
#[property(resolve, fold)]
|
#[property(resolve, fold)]
|
||||||
pub const OUTSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
pub const OUTSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
||||||
|
|
||||||
/// How much to round the shape's corners.
|
/// How much to round the shape's corners.
|
||||||
#[property(resolve, fold)]
|
#[property(skip, resolve, fold)]
|
||||||
pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
|
||||||
|
|
||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
@ -57,14 +55,11 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(Content::inline(
|
Ok(Content::inline(
|
||||||
Self(args.find()?).pack().sized(Spec::new(width, height)),
|
Self(args.eat()?).pack().sized(Spec::new(width, height)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
fn set(...) {
|
||||||
let mut styles = StyleMap::new();
|
|
||||||
styles.set_opt(Self::FILL, args.named("fill")?);
|
|
||||||
|
|
||||||
if is_round(S) {
|
if is_round(S) {
|
||||||
styles.set_opt(
|
styles.set_opt(
|
||||||
Self::STROKE,
|
Self::STROKE,
|
||||||
@ -73,16 +68,8 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
styles.set_opt(Self::STROKE, args.named("stroke")?);
|
styles.set_opt(Self::STROKE, args.named("stroke")?);
|
||||||
}
|
|
||||||
|
|
||||||
styles.set_opt(Self::INSET, args.named("inset")?);
|
|
||||||
styles.set_opt(Self::OUTSET, args.named("outset")?);
|
|
||||||
|
|
||||||
if !is_round(S) {
|
|
||||||
styles.set_opt(Self::RADIUS, args.named("radius")?);
|
styles.set_opt(Self::RADIUS, args.named("radius")?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(styles)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ impl BoxNode {
|
|||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
let width = args.named("width")?;
|
let width = args.named("width")?;
|
||||||
let height = args.named("height")?;
|
let height = args.named("height")?;
|
||||||
let body: LayoutNode = args.find()?.unwrap_or_default();
|
let body: LayoutNode = args.eat()?.unwrap_or_default();
|
||||||
Ok(Content::inline(body.sized(Spec::new(width, height))))
|
Ok(Content::inline(body.sized(Spec::new(width, height))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,6 +19,6 @@ pub struct BlockNode;
|
|||||||
#[node]
|
#[node]
|
||||||
impl BlockNode {
|
impl BlockNode {
|
||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
Ok(Content::Block(args.find()?.unwrap_or_default()))
|
Ok(Content::Block(args.eat()?.unwrap_or_default()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ pub struct PadNode {
|
|||||||
#[node]
|
#[node]
|
||||||
impl PadNode {
|
impl PadNode {
|
||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
let all = args.find()?;
|
let all = args.named("rest")?.or(args.find()?);
|
||||||
let x = args.named("x")?;
|
let x = args.named("x")?;
|
||||||
let y = args.named("y")?;
|
let y = args.named("y")?;
|
||||||
let left = args.named("left")?.or(x).or(all).unwrap_or_default();
|
let left = args.named("left")?.or(x).or(all).unwrap_or_default();
|
||||||
|
@ -39,24 +39,11 @@ impl PageNode {
|
|||||||
Ok(Content::Page(Self(args.expect("body")?)))
|
Ok(Content::Page(Self(args.expect("body")?)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
fn set(...) {
|
||||||
let mut styles = StyleMap::new();
|
|
||||||
|
|
||||||
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
|
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
|
||||||
styles.set(Self::WIDTH, Smart::Custom(paper.width().into()));
|
styles.set(Self::WIDTH, Smart::Custom(paper.width().into()));
|
||||||
styles.set(Self::HEIGHT, Smart::Custom(paper.height().into()));
|
styles.set(Self::HEIGHT, Smart::Custom(paper.height().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
styles.set_opt(Self::WIDTH, args.named("width")?);
|
|
||||||
styles.set_opt(Self::HEIGHT, args.named("height")?);
|
|
||||||
styles.set_opt(Self::MARGINS, args.named("margins")?);
|
|
||||||
styles.set_opt(Self::FLIPPED, args.named("flipped")?);
|
|
||||||
styles.set_opt(Self::FILL, args.named("fill")?);
|
|
||||||
styles.set_opt(Self::COLUMNS, args.named("columns")?);
|
|
||||||
styles.set_opt(Self::HEADER, args.named("header")?);
|
|
||||||
styles.set_opt(Self::FOOTER, args.named("footer")?);
|
|
||||||
|
|
||||||
Ok(styles)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ impl LinkNode {
|
|||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
Ok(Content::show(Self {
|
Ok(Content::show(Self {
|
||||||
url: args.expect::<EcoString>("url")?,
|
url: args.expect::<EcoString>("url")?,
|
||||||
body: args.find()?,
|
body: args.eat()?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ pub struct TextNode;
|
|||||||
#[node]
|
#[node]
|
||||||
impl TextNode {
|
impl TextNode {
|
||||||
/// A prioritized sequence of font families.
|
/// A prioritized sequence of font families.
|
||||||
#[property(referenced, variadic)]
|
#[property(skip, referenced)]
|
||||||
pub const FAMILY: Vec<FontFamily> = vec![FontFamily::new("IBM Plex Sans")];
|
pub const FAMILY: Vec<FontFamily> = vec![FontFamily::new("IBM Plex Sans")];
|
||||||
/// Whether to allow font fallback when the primary font list contains no
|
/// Whether to allow font fallback when the primary font list contains no
|
||||||
/// match.
|
/// match.
|
||||||
@ -109,22 +109,22 @@ impl TextNode {
|
|||||||
pub const FEATURES: Vec<(Tag, u32)> = vec![];
|
pub const FEATURES: Vec<(Tag, u32)> = vec![];
|
||||||
|
|
||||||
/// Whether the font weight should be increased by 300.
|
/// Whether the font weight should be increased by 300.
|
||||||
#[property(hidden, fold)]
|
#[property(skip, fold)]
|
||||||
pub const STRONG: Toggle = false;
|
pub const STRONG: Toggle = false;
|
||||||
/// Whether the the font style should be inverted.
|
/// Whether the the font style should be inverted.
|
||||||
#[property(hidden, fold)]
|
#[property(skip, fold)]
|
||||||
pub const EMPH: Toggle = false;
|
pub const EMPH: Toggle = false;
|
||||||
/// A case transformation that should be applied to the text.
|
/// A case transformation that should be applied to the text.
|
||||||
#[property(hidden)]
|
#[property(skip)]
|
||||||
pub const CASE: Option<Case> = None;
|
pub const CASE: Option<Case> = None;
|
||||||
/// Whether small capital glyphs should be used. ("smcp")
|
/// Whether small capital glyphs should be used. ("smcp")
|
||||||
#[property(hidden)]
|
#[property(skip)]
|
||||||
pub const SMALLCAPS: bool = false;
|
pub const SMALLCAPS: bool = false;
|
||||||
/// An URL the text should link to.
|
/// An URL the text should link to.
|
||||||
#[property(hidden, referenced)]
|
#[property(skip, referenced)]
|
||||||
pub const LINK: Option<EcoString> = None;
|
pub const LINK: Option<EcoString> = None;
|
||||||
/// Decorative lines.
|
/// Decorative lines.
|
||||||
#[property(hidden, fold)]
|
#[property(skip, fold)]
|
||||||
pub const DECO: Decoration = vec![];
|
pub const DECO: Decoration = vec![];
|
||||||
|
|
||||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||||
@ -133,6 +133,36 @@ impl TextNode {
|
|||||||
// styles all text in it.
|
// styles all text in it.
|
||||||
args.expect("body")
|
args.expect("body")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set(...) {
|
||||||
|
if let Some(family) = args.named("family")? {
|
||||||
|
styles.set(Self::FAMILY, family);
|
||||||
|
} else {
|
||||||
|
let mut count = 0;
|
||||||
|
let mut content = false;
|
||||||
|
for item in args.items.iter().filter(|item| item.name.is_none()) {
|
||||||
|
if EcoString::is(&item.value) {
|
||||||
|
count += 1;
|
||||||
|
} else if Content::is(&item.value) {
|
||||||
|
content = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the final string if it's needed as the body.
|
||||||
|
if constructor && !content && count > 0 {
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
let mut list = Vec::with_capacity(count);
|
||||||
|
for _ in 0 .. count {
|
||||||
|
list.push(args.find()?.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.set(Self::FAMILY, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A font family like "Arial".
|
/// A font family like "Arial".
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 21 KiB |
@ -49,8 +49,8 @@ Some more text.
|
|||||||
Another text.
|
Another text.
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 18-22 expected content, found string
|
// Error: 18-22 expected content, found integer
|
||||||
#show heading as "hi"
|
#show heading as 1234
|
||||||
= Heading
|
= Heading
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -38,6 +38,14 @@ Emoji: 🐪, 🌋, 🏞
|
|||||||
#set text("PT Sans", "Twitter Color Emoji", fallback: false)
|
#set text("PT Sans", "Twitter Color Emoji", fallback: false)
|
||||||
2π = 𝛼 + 𝛽. ✅
|
2π = 𝛼 + 𝛽. ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test string body.
|
||||||
|
#text("Text") \
|
||||||
|
#text(red, "Text") \
|
||||||
|
#text("Ubuntu", blue, "Text") \
|
||||||
|
#text([Text], teal, "IBM Plex Serif") \
|
||||||
|
#text(forest, "Latin Modern Roman", [Text]) \
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 11-16 unexpected argument
|
// Error: 11-16 unexpected argument
|
||||||
#set text(false)
|
#set text(false)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user