Less style properties

This commit is contained in:
Laurenz 2022-11-08 11:45:54 +01:00
parent 0a41844cc4
commit a7a4cae294
12 changed files with 84 additions and 195 deletions

View File

@ -7,7 +7,7 @@ use std::fmt::Write;
use self::tex::{layout_tex, Texify};
use crate::layout::BlockSpacing;
use crate::prelude::*;
use crate::text::FontFamily;
use crate::text::{FallbackList, FontFamily, TextNode};
/// A piece of a mathematical formula.
#[derive(Debug, Clone, Hash)]
@ -20,9 +20,6 @@ pub struct MathNode {
#[node(Show, Finalize, LayoutInline, Texify)]
impl MathNode {
/// The math font family.
#[property(referenced)]
pub const FAMILY: FontFamily = FontFamily::new("NewComputerModernMath");
/// The spacing above display math.
#[property(resolve, shorthand(around))]
pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
@ -44,11 +41,7 @@ impl Show for MathNode {
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(if self.display {
self.clone().pack().aligned(Axes::with_x(Some(Align::Center.into())))
} else {
self.clone().pack()
})
Ok(self.clone().pack())
}
}
@ -57,13 +50,20 @@ impl Finalize for MathNode {
&self,
_: Tracked<dyn World>,
styles: StyleChain,
realized: Content,
mut realized: Content,
) -> SourceResult<Content> {
Ok(if self.display {
realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW))
} else {
realized
})
realized = realized.styled(
TextNode::FAMILY,
FallbackList(vec![FontFamily::new("NewComputerModernMath")]),
);
if self.display {
realized = realized
.aligned(Axes::with_x(Some(Align::Center.into())))
.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW))
}
Ok(realized)
}
}

View File

@ -5,9 +5,8 @@ use rex::parser::color::RGBA;
use rex::render::{Backend, Cursor, Renderer};
use typst::font::Font;
use super::MathNode;
use crate::prelude::*;
use crate::text::{variant, LinebreakNode, SpaceNode, TextNode};
use crate::text::{families, variant, LinebreakNode, SpaceNode, TextNode};
/// Turn a math node into TeX math code.
#[capability]
@ -42,17 +41,21 @@ pub fn layout_tex(
styles: StyleChain,
) -> SourceResult<Frame> {
// Load the font.
let font = world
.book()
.select(styles.get(MathNode::FAMILY).as_str(), variant(styles))
.and_then(|id| world.font(id))
.expect("failed to find math font");
let variant = variant(styles);
let mut font = None;
for family in families(styles) {
font = world.book().select(family, variant).and_then(|id| world.font(id));
if font.as_ref().map_or(false, |font| font.math().is_some()) {
break;
}
}
// Prepare the font context.
let font = font.expect("failed to find suitable math font");
let ctx = font
.math()
.map(|math| FontContext::new(font.ttf(), math))
.expect("font is not suitable for math");
.expect("failed to create font context");
// Layout the formula.
let em = styles.get(TextNode::SIZE);

View File

@ -1,6 +1,8 @@
use typst::font::FontWeight;
use crate::layout::{BlockNode, BlockSpacing};
use crate::prelude::*;
use crate::text::{FontFamily, TextNode, TextSize};
use crate::text::{TextNode, TextSize};
/// A section heading.
#[derive(Debug, Hash)]
@ -14,32 +16,10 @@ pub struct HeadingNode {
#[node(Show, Finalize)]
impl HeadingNode {
/// The heading's font family. Just the normal text family if `auto`.
#[property(referenced)]
pub const FAMILY: Leveled<Smart<FontFamily>> = Leveled::Value(Smart::Auto);
/// The color of text in the heading. Just the normal text color if `auto`.
#[property(referenced)]
pub const FILL: Leveled<Smart<Paint>> = Leveled::Value(Smart::Auto);
/// The size of text in the heading.
#[property(referenced)]
pub const SIZE: Leveled<TextSize> = Leveled::Mapping(|level| {
let size = match level.get() {
1 => 1.4,
2 => 1.2,
_ => 1.0,
};
TextSize(Em::new(size).into())
});
/// Whether text in the heading is strengthend.
#[property(referenced)]
pub const STRONG: Leveled<bool> = Leveled::Value(true);
/// Whether text in the heading is emphasized.
#[property(referenced)]
pub const EMPH: Leveled<bool> = Leveled::Value(false);
/// Whether the heading is underlined.
#[property(referenced)]
pub const UNDERLINE: Leveled<bool> = Leveled::Value(false);
/// Whether the heading appears in the outline.
pub const OUTLINED: bool = true;
/// Whether the heading is numbered.
pub const NUMBERED: bool = true;
/// The spacing above the heading.
#[property(referenced, shorthand(around))]
@ -55,11 +35,6 @@ impl HeadingNode {
pub const BELOW: Leveled<Option<BlockSpacing>> =
Leveled::Value(Some(Ratio::new(0.55).into()));
/// Whether the heading appears in the outline.
pub const OUTLINED: bool = true;
/// Whether the heading is numbered.
pub const NUMBERED: bool = true;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self {
body: args.expect("body")?,
@ -101,30 +76,17 @@ impl Finalize for HeadingNode {
}
let mut map = StyleMap::new();
map.set(TextNode::SIZE, resolve!(Self::SIZE));
map.set(TextNode::SIZE, {
let size = match self.level.get() {
1 => 1.4,
2 => 1.2,
_ => 1.0,
};
TextSize(Em::new(size).into())
});
map.set(TextNode::WEIGHT, FontWeight::BOLD);
if let Smart::Custom(family) = resolve!(Self::FAMILY) {
map.set_family(family, styles);
}
if let Smart::Custom(fill) = resolve!(Self::FILL) {
map.set(TextNode::FILL, fill);
}
if resolve!(Self::STRONG) {
realized = realized.strong();
}
if resolve!(Self::EMPH) {
realized = realized.emph();
}
if resolve!(Self::UNDERLINE) {
realized = realized.underlined();
}
realized = realized.styled_with_map(map);
realized = realized.spaced(
realized = realized.styled_with_map(map).spaced(
resolve!(Self::ABOVE).resolve(styles),
resolve!(Self::BELOW).resolve(styles),
);

View File

@ -7,31 +7,34 @@ pub struct LinkNode {
/// The destination the link points to.
pub dest: Destination,
/// How the link is represented.
pub body: Option<Content>,
pub body: Content,
}
impl LinkNode {
/// Create a link node from a URL with its bare text.
pub fn from_url(url: EcoString) -> Self {
Self { dest: Destination::Url(url), body: None }
let mut text = url.as_str();
for prefix in ["mailto:", "tel:"] {
text = text.trim_start_matches(prefix);
}
let shorter = text.len() < url.len();
let body = TextNode::packed(if shorter { text.into() } else { url.clone() });
Self { dest: Destination::Url(url), body }
}
}
#[node(Show, Finalize)]
impl LinkNode {
/// The fill color of text in the link. Just the surrounding text color
/// if `auto`.
pub const FILL: Smart<Paint> = Smart::Auto;
/// Whether to underline the link.
pub const UNDERLINE: Smart<bool> = Smart::Auto;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
let dest = args.expect::<Destination>("destination")?;
let body = match dest {
Destination::Url(_) => args.eat()?,
Destination::Internal(_) => Some(args.expect("body")?),
};
Ok(Self { dest, body }.pack())
Ok(match dest {
Destination::Url(url) => match args.eat()? {
Some(body) => Self { dest: Destination::Url(url), body },
None => Self::from_url(url),
},
Destination::Internal(_) => Self { dest, body: args.expect("body")? },
}
.pack())
}
fn field(&self, name: &str) -> Option<Value> {
@ -40,10 +43,7 @@ impl LinkNode {
Destination::Url(url) => Value::Str(url.clone().into()),
Destination::Internal(loc) => Value::Dict(loc.encode()),
}),
"body" => Some(match &self.body {
Some(body) => Value::Content(body.clone()),
None => Value::None,
}),
"body" => Some(Value::Content(self.body.clone())),
_ => None,
}
}
@ -53,23 +53,13 @@ impl Show for LinkNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self {
dest: self.dest.clone(),
body: self.body.as_ref().map(|body| body.unguard(id)),
body: self.body.unguard(id),
}
.pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.body.clone().unwrap_or_else(|| match &self.dest {
Destination::Url(url) => {
let mut text = url.as_str();
for prefix in ["mailto:", "tel:"] {
text = text.trim_start_matches(prefix);
}
let shorter = text.len() < url.len();
TextNode::packed(if shorter { text.into() } else { url.clone() })
}
Destination::Internal(_) => Content::empty(),
}))
Ok(self.body.clone())
}
}
@ -77,26 +67,9 @@ impl Finalize for LinkNode {
fn finalize(
&self,
_: Tracked<dyn World>,
styles: StyleChain,
mut realized: Content,
_: StyleChain,
realized: Content,
) -> SourceResult<Content> {
let mut map = StyleMap::new();
map.set(TextNode::LINK, Some(self.dest.clone()));
if let Smart::Custom(fill) = styles.get(Self::FILL) {
map.set(TextNode::FILL, fill);
}
if match styles.get(Self::UNDERLINE) {
Smart::Auto => match &self.dest {
Destination::Url(_) => true,
Destination::Internal(_) => false,
},
Smart::Custom(underline) => underline,
} {
realized = realized.underlined();
}
Ok(realized.styled_with_map(map))
Ok(realized.styled(TextNode::LINK, Some(self.dest.clone())))
}
}

View File

@ -6,7 +6,7 @@ use syntect::highlighting::{
use syntect::parsing::SyntaxSet;
use typst::syntax;
use super::{FontFamily, Hyphenate, LinebreakNode, TextNode};
use super::{FallbackList, FontFamily, Hyphenate, LinebreakNode, TextNode};
use crate::layout::{BlockNode, BlockSpacing};
use crate::prelude::*;
@ -24,9 +24,6 @@ impl RawNode {
/// The language to syntax-highlight in.
#[property(referenced)]
pub const LANG: Option<EcoString> = None;
/// The raw text's font family.
#[property(referenced)]
pub const FAMILY: FontFamily = FontFamily::new("IBM Plex Mono");
/// The spacing above block-level raw.
#[property(resolve, shorthand(around))]
pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
@ -119,14 +116,16 @@ impl Finalize for RawNode {
styles: StyleChain,
mut realized: Content,
) -> SourceResult<Content> {
let mut map = StyleMap::new();
map.set_family(styles.get(Self::FAMILY).clone(), styles);
realized = realized.styled(
TextNode::FAMILY,
FallbackList(vec![FontFamily::new("IBM Plex Mono")]),
);
if self.block {
realized = realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW));
}
Ok(realized.styled_with_map(map))
Ok(realized)
}
}

View File

@ -552,7 +552,7 @@ pub fn variant(styles: StyleChain) -> FontVariant {
}
/// Resolve a prioritized iterator over the font families.
fn families(styles: StyleChain) -> impl Iterator<Item = &str> + Clone {
pub fn families(styles: StyleChain) -> impl Iterator<Item = &str> + Clone {
const FALLBACKS: &[&str] = &[
"ibm plex sans",
"twitter color emoji",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -43,11 +43,10 @@ multiline.
---
// Test styling.
#show heading.where(level: 5): it => {
text(family: "Roboto", fill: eastern, it.body + [!])
}
= Heading
#set heading(family: "Roboto", fill: eastern)
#show heading: it => it.body
#show strong: it => it.body + [!]
===== Heading 🌍
#heading(level: 5)[Heading]

View File

@ -1,49 +0,0 @@
// Test styles with closure.
---
#set heading(
size: 10pt,
around: 0.65em,
fill: lvl => if even(lvl) { red } else { blue },
)
= Heading 1
== Heading 2
=== Heading 3
==== Heading 4
---
// Test in constructor.
#heading(
level: 3,
size: 10pt,
strong: lvl => {
assert(lvl == 3)
false
}
)[Level 3]
---
// Error: 22-26 expected string or auto or function, found length
#set heading(family: 10pt)
= Heading
---
// Error: 29-38 cannot add integer and string
#set heading(strong: lvl => lvl + "2")
= Heading
---
// Error: 22-34 expected string or auto, found boolean
#set heading(family: lvl => false)
= Heading
---
// Error: 22-37 missing argument: b
#set heading(family: (a, b) => a + b)
= Heading
---
// Error: 22-30 unexpected argument
#set heading(family: () => {})
= Heading

View File

@ -13,8 +13,9 @@
---
// Test full reset.
#set heading(size: 1em, strong: false, around: none)
#set heading(around: none)
#show heading: [B]
#show heading: text.with(size: 10pt, weight: 400)
A [= Heading] C
---
@ -28,8 +29,8 @@ my heading?
---
// Test integrated example.
#set heading(size: 1em)
#show heading: it => {
set text(10pt)
move(dy: -1pt)[📖]
h(5pt)
if it.level == 1 {

View File

@ -2,7 +2,8 @@
---
#set par(indent: 12pt, leading: 5pt)
#set heading(size: 10pt, above: 8pt)
#set heading(above: 8pt)
#show heading: text.with(size: 10pt)
The first paragraph has no indent.

View File

@ -16,6 +16,7 @@ call #link("tel:123") for more information.
---
// Test that the period is trimmed.
#show link: underline
https://a.b.?q=%10#. \
Wahttp://link \
Nohttps:\//link \
@ -23,20 +24,19 @@ Nohttp\://comment
---
// Styled with underline and color.
#set link(fill: rgb("283663"))
#show link: it => underline(text(fill: rgb("283663"), it))
You could also make the
#link("https://html5zombo.com/")[link look way more typical.]
---
// Transformed link.
#set page(height: 60pt)
#set link(underline: false)
#let mylink = link("https://typst.org/")[LINK]
My cool #move(dx: 0.7cm, dy: 0.7cm, rotate(10deg, scale(200%, mylink)))
---
// Link containing a block.
#link("https://example.com/", underline: false, block[
#link("https://example.com/", block[
My cool rhino
#move(dx: 10pt, image("/res/rhino.png", width: 1cm))
])