summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-08 11:45:54 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-08 11:45:59 +0100
commita7a4cae2948176119e8995bd8e1868f2d0e65029 (patch)
tree123e8867c939373c1c5ce5a0d20f55192c2467c2
parent0a41844cc4e645e87fe48aa31ed3a4fd40a6ab11 (diff)
Less style properties
-rw-r--r--library/src/math/mod.rs30
-rw-r--r--library/src/math/tex.rs19
-rw-r--r--library/src/structure/heading.rs72
-rw-r--r--library/src/text/link.rs71
-rw-r--r--library/src/text/raw.rs13
-rw-r--r--library/src/text/shaping.rs2
-rw-r--r--tests/ref/text/link.pngbin49564 -> 49393 bytes
-rw-r--r--tests/typ/structure/heading.typ9
-rw-r--r--tests/typ/style/closure.typ49
-rw-r--r--tests/typ/style/show-node.typ5
-rw-r--r--tests/typ/text/indent.typ3
-rw-r--r--tests/typ/text/link.typ6
12 files changed, 84 insertions, 195 deletions
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index a89b4953..ae3c4a9a 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -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)
}
}
diff --git a/library/src/math/tex.rs b/library/src/math/tex.rs
index 7de68d7b..f924ebbe 100644
--- a/library/src/math/tex.rs
+++ b/library/src/math/tex.rs
@@ -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);
diff --git a/library/src/structure/heading.rs b/library/src/structure/heading.rs
index f93be5d9..fe9b9013 100644
--- a/library/src/structure/heading.rs
+++ b/library/src/structure/heading.rs
@@ -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));
-
- 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();
- }
+ 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);
- 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),
);
diff --git a/library/src/text/link.rs b/library/src/text/link.rs
index b74ca530..acd37df6 100644
--- a/library/src/text/link.rs
+++ b/library/src/text/link.rs
@@ -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())))
}
}
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index c6229d59..2041b25e 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -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)
}
}
diff --git a/library/src/text/shaping.rs b/library/src/text/shaping.rs
index b67ce411..ac7218a0 100644
--- a/library/src/text/shaping.rs
+++ b/library/src/text/shaping.rs
@@ -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",
diff --git a/tests/ref/text/link.png b/tests/ref/text/link.png
index 59e62bea..9d5cb054 100644
--- a/tests/ref/text/link.png
+++ b/tests/ref/text/link.png
Binary files differ
diff --git a/tests/typ/structure/heading.typ b/tests/typ/structure/heading.typ
index d08b3687..b552f6c3 100644
--- a/tests/typ/structure/heading.typ
+++ b/tests/typ/structure/heading.typ
@@ -43,11 +43,10 @@ multiline.
---
// Test styling.
-= Heading
-
-#set heading(family: "Roboto", fill: eastern)
-#show heading: it => it.body
-#show strong: it => it.body + [!]
+#show heading.where(level: 5): it => {
+ text(family: "Roboto", fill: eastern, it.body + [!])
+}
+= Heading
===== Heading 🌍
#heading(level: 5)[Heading]
diff --git a/tests/typ/style/closure.typ b/tests/typ/style/closure.typ
deleted file mode 100644
index cd1f87df..00000000
--- a/tests/typ/style/closure.typ
+++ /dev/null
@@ -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
diff --git a/tests/typ/style/show-node.typ b/tests/typ/style/show-node.typ
index e552038d..4b0542e5 100644
--- a/tests/typ/style/show-node.typ
+++ b/tests/typ/style/show-node.typ
@@ -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 {
diff --git a/tests/typ/text/indent.typ b/tests/typ/text/indent.typ
index 92b12180..97044153 100644
--- a/tests/typ/text/indent.typ
+++ b/tests/typ/text/indent.typ
@@ -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.
diff --git a/tests/typ/text/link.typ b/tests/typ/text/link.typ
index 8e777956..8c3e6ddd 100644
--- a/tests/typ/text/link.typ
+++ b/tests/typ/text/link.typ
@@ -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))
])