Merge branch 'main' into krilla-port
# Conflicts: # crates/typst-cli/src/compile.rs # crates/typst-pdf/src/catalog.rs # crates/typst-pdf/src/color.rs # crates/typst-pdf/src/content.rs # crates/typst-pdf/src/lib.rs # crates/typst-pdf/src/resources.rs # crates/typst-pdf/src/tiling.rs
@ -123,6 +123,36 @@ pub fn named_items<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(v) = parent.cast::<ast::Closure>().filter(|v| {
|
||||||
|
// Check if the node is in the body of the closure.
|
||||||
|
let body = parent.find(v.body().span());
|
||||||
|
body.is_some_and(|n| n.find(node.span()).is_some())
|
||||||
|
}) {
|
||||||
|
for param in v.params().children() {
|
||||||
|
match param {
|
||||||
|
ast::Param::Pos(pattern) => {
|
||||||
|
for ident in pattern.bindings() {
|
||||||
|
if let Some(t) = recv(NamedItem::Var(ident)) {
|
||||||
|
return Some(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Param::Named(n) => {
|
||||||
|
if let Some(t) = recv(NamedItem::Var(n.name())) {
|
||||||
|
return Some(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Param::Spread(s) => {
|
||||||
|
if let Some(sink_ident) = s.sink_ident() {
|
||||||
|
if let Some(t) = recv(NamedItem::Var(sink_ident)) {
|
||||||
|
return Some(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ancestor = Some(parent.clone());
|
ancestor = Some(parent.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -269,6 +299,17 @@ mod tests {
|
|||||||
assert!(!has_named_items(r#"#let a = 1;#let b = 2;"#, 8, "b"));
|
assert!(!has_named_items(r#"#let a = 1;#let b = 2;"#, 8, "b"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_param_named_items() {
|
||||||
|
// Has named items
|
||||||
|
assert!(has_named_items(r#"#let f(a) = 1;#let b = 2;"#, 12, "a"));
|
||||||
|
assert!(has_named_items(r#"#let f(a: b) = 1;#let b = 2;"#, 15, "a"));
|
||||||
|
|
||||||
|
// Doesn't have named items
|
||||||
|
assert!(!has_named_items(r#"#let f(a) = 1;#let b = 2;"#, 19, "a"));
|
||||||
|
assert!(!has_named_items(r#"#let f(a: b) = 1;#let b = 2;"#, 15, "b"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import_named_items() {
|
fn test_import_named_items() {
|
||||||
// Cannot test much.
|
// Cannot test much.
|
||||||
|
@ -17,7 +17,7 @@ pub fn distribute(composer: &mut Composer, regions: Regions) -> FlowResult<Frame
|
|||||||
regions,
|
regions,
|
||||||
items: vec![],
|
items: vec![],
|
||||||
sticky: None,
|
sticky: None,
|
||||||
stickable: false,
|
stickable: None,
|
||||||
};
|
};
|
||||||
let init = distributor.snapshot();
|
let init = distributor.snapshot();
|
||||||
let forced = match distributor.run() {
|
let forced = match distributor.run() {
|
||||||
@ -42,9 +42,26 @@ struct Distributor<'a, 'b, 'x, 'y, 'z> {
|
|||||||
/// A snapshot which can be restored to migrate a suffix of sticky blocks to
|
/// A snapshot which can be restored to migrate a suffix of sticky blocks to
|
||||||
/// the next region.
|
/// the next region.
|
||||||
sticky: Option<DistributionSnapshot<'a, 'b>>,
|
sticky: Option<DistributionSnapshot<'a, 'b>>,
|
||||||
/// Whether there was at least one proper block. Otherwise, sticky blocks
|
/// Whether the current group of consecutive sticky blocks are still sticky
|
||||||
/// are disabled (or else they'd keep being migrated).
|
/// and may migrate with the attached frame. This is `None` while we aren't
|
||||||
stickable: bool,
|
/// processing sticky blocks. On the first sticky block, this will become
|
||||||
|
/// `Some(true)` if migrating sticky blocks as usual would make a
|
||||||
|
/// difference - this is given by `regions.may_progress()`. Otherwise, it
|
||||||
|
/// is set to `Some(false)`, which is usually the case when the first
|
||||||
|
/// sticky block in the group is at the very top of the page (then,
|
||||||
|
/// migrating it would just lead us back to the top of the page, leading
|
||||||
|
/// to an infinite loop). In that case, all sticky blocks of the group are
|
||||||
|
/// also disabled, until this is reset to `None` on the first non-sticky
|
||||||
|
/// frame we find.
|
||||||
|
///
|
||||||
|
/// While this behavior of disabling stickiness of sticky blocks at the
|
||||||
|
/// very top of the page may seem non-ideal, it is only problematic (that
|
||||||
|
/// is, may lead to orphaned sticky blocks / headings) if the combination
|
||||||
|
/// of 'sticky blocks + attached frame' doesn't fit in one page, in which
|
||||||
|
/// case there is nothing Typst can do to improve the situation, as sticky
|
||||||
|
/// blocks are supposed to always be in the same page as the subsequent
|
||||||
|
/// frame, but that is impossible in that case, which is thus pathological.
|
||||||
|
stickable: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A snapshot of the distribution state.
|
/// A snapshot of the distribution state.
|
||||||
@ -314,13 +331,31 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
|
|||||||
// If the frame is sticky and we haven't remembered a preceding
|
// If the frame is sticky and we haven't remembered a preceding
|
||||||
// sticky element, make a checkpoint which we can restore should we
|
// sticky element, make a checkpoint which we can restore should we
|
||||||
// end on this sticky element.
|
// end on this sticky element.
|
||||||
if self.stickable && self.sticky.is_none() {
|
//
|
||||||
|
// The first sticky block within consecutive sticky blocks
|
||||||
|
// determines whether this group of sticky blocks has stickiness
|
||||||
|
// disabled or not.
|
||||||
|
//
|
||||||
|
// The criteria used here is: if migrating this group of sticky
|
||||||
|
// blocks together with the "attached" block can't improve the lack
|
||||||
|
// of space, since we're at the start of the region, then we don't
|
||||||
|
// do so, and stickiness is disabled (at least, for this region).
|
||||||
|
// Otherwise, migration is allowed.
|
||||||
|
//
|
||||||
|
// Note that, since the whole region is checked, this ensures sticky
|
||||||
|
// blocks at the top of a block - but not necessarily of the page -
|
||||||
|
// can still be migrated.
|
||||||
|
if self.sticky.is_none()
|
||||||
|
&& *self.stickable.get_or_insert_with(|| self.regions.may_progress())
|
||||||
|
{
|
||||||
self.sticky = Some(self.snapshot());
|
self.sticky = Some(self.snapshot());
|
||||||
}
|
}
|
||||||
} else if !frame.is_empty() {
|
} else if !frame.is_empty() {
|
||||||
// If the frame isn't sticky, we can forget a previous snapshot.
|
// If the frame isn't sticky, we can forget a previous snapshot. We
|
||||||
self.stickable = true;
|
// interrupt a group of sticky blocks, if there was one, so we reset
|
||||||
|
// the saved stickable check for the next group of sticky blocks.
|
||||||
self.sticky = None;
|
self.sticky = None;
|
||||||
|
self.stickable = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle footnotes.
|
// Handle footnotes.
|
||||||
|
@ -74,6 +74,7 @@ pub fn layout_enum(
|
|||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let numbering = elem.numbering(styles);
|
let numbering = elem.numbering(styles);
|
||||||
|
let reversed = elem.reversed(styles);
|
||||||
let indent = elem.indent(styles);
|
let indent = elem.indent(styles);
|
||||||
let body_indent = elem.body_indent(styles);
|
let body_indent = elem.body_indent(styles);
|
||||||
let gutter = elem.spacing(styles).unwrap_or_else(|| {
|
let gutter = elem.spacing(styles).unwrap_or_else(|| {
|
||||||
@ -86,7 +87,9 @@ pub fn layout_enum(
|
|||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
let mut locator = locator.split();
|
let mut locator = locator.split();
|
||||||
let mut number = elem.start(styles);
|
let mut number =
|
||||||
|
elem.start(styles)
|
||||||
|
.unwrap_or_else(|| if reversed { elem.children.len() } else { 1 });
|
||||||
let mut parents = EnumElem::parents_in(styles);
|
let mut parents = EnumElem::parents_in(styles);
|
||||||
|
|
||||||
let full = elem.full(styles);
|
let full = elem.full(styles);
|
||||||
@ -127,7 +130,8 @@ pub fn layout_enum(
|
|||||||
item.body.clone().styled(EnumElem::set_parents(smallvec![number])),
|
item.body.clone().styled(EnumElem::set_parents(smallvec![number])),
|
||||||
locator.next(&item.body.span()),
|
locator.next(&item.body.span()),
|
||||||
));
|
));
|
||||||
number = number.saturating_add(1);
|
number =
|
||||||
|
if reversed { number.saturating_sub(1) } else { number.saturating_add(1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
let grid = CellGrid::new(
|
let grid = CellGrid::new(
|
||||||
|
@ -33,8 +33,17 @@ pub fn layout_repeat(
|
|||||||
let fill = region.size.x;
|
let fill = region.size.x;
|
||||||
let width = piece.width();
|
let width = piece.width();
|
||||||
|
|
||||||
// count * width + (count - 1) * gap = fill, but count is an integer so
|
// We need to fit the body N times, but the number of gaps is (N - 1):
|
||||||
// we need to round down and get the remainder.
|
// N * w + (N - 1) * g ≤ F
|
||||||
|
// where N - body count (count)
|
||||||
|
// w - body width (width)
|
||||||
|
// g - gap width (gap)
|
||||||
|
// F - available space to fill (fill)
|
||||||
|
//
|
||||||
|
// N * w + N * g - g ≤ F
|
||||||
|
// N * (w + g) ≤ F + g
|
||||||
|
// N ≤ (F + g) / (w + g)
|
||||||
|
// N = ⌊(F + g) / (w + g)⌋
|
||||||
let count = ((fill + gap) / (width + gap)).floor();
|
let count = ((fill + gap) / (width + gap)).floor();
|
||||||
let remaining = (fill + gap) % (width + gap);
|
let remaining = (fill + gap) % (width + gap);
|
||||||
|
|
||||||
@ -52,7 +61,7 @@ pub fn layout_repeat(
|
|||||||
if width > Abs::zero() {
|
if width > Abs::zero() {
|
||||||
for _ in 0..(count as usize).min(1000) {
|
for _ in 0..(count as usize).min(1000) {
|
||||||
frame.push_frame(Point::with_x(offset), piece.clone());
|
frame.push_frame(Point::with_x(offset), piece.clone());
|
||||||
offset += piece.width() + gap;
|
offset += width + gap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,11 +719,7 @@ fn segment(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
let solid = stroke
|
let solid = stroke.dash.as_ref().map(|dash| dash.array.is_empty()).unwrap_or(true);
|
||||||
.dash
|
|
||||||
.as_ref()
|
|
||||||
.map(|pattern| pattern.array.is_empty())
|
|
||||||
.unwrap_or(true);
|
|
||||||
|
|
||||||
let use_fill = solid && fill_corners(start, end, corners);
|
let use_fill = solid && fill_corners(start, end, corners);
|
||||||
let shape = if use_fill {
|
let shape = if use_fill {
|
||||||
|
@ -119,7 +119,6 @@ pub(super) fn define(global: &mut Scope, inputs: Dict, features: &Features) {
|
|||||||
global.define_func::<panic>();
|
global.define_func::<panic>();
|
||||||
global.define_func::<assert>();
|
global.define_func::<assert>();
|
||||||
global.define_func::<eval>();
|
global.define_func::<eval>();
|
||||||
global.define_func::<style>();
|
|
||||||
if features.is_enabled(Feature::Html) {
|
if features.is_enabled(Feature::Html) {
|
||||||
global.define_func::<target>();
|
global.define_func::<target>();
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,6 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||||||
(Symbol(a), Content(b)) => Content(TextElem::packed(a.get()) + b),
|
(Symbol(a), Content(b)) => Content(TextElem::packed(a.get()) + b),
|
||||||
(Array(a), Array(b)) => Array(a + b),
|
(Array(a), Array(b)) => Array(a + b),
|
||||||
(Dict(a), Dict(b)) => Dict(a + b),
|
(Dict(a), Dict(b)) => Dict(a + b),
|
||||||
|
|
||||||
// Type compatibility.
|
|
||||||
(Type(a), Str(b)) => Str(format_str!("{a}{b}")),
|
|
||||||
(Str(a), Type(b)) => Str(format_str!("{a}{b}")),
|
|
||||||
|
|
||||||
(a, b) => mismatch!("cannot join {} with {}", a, b),
|
(a, b) => mismatch!("cannot join {} with {}", a, b),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -149,18 +144,14 @@ pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||||||
| (Length(thickness), Gradient(gradient)) => {
|
| (Length(thickness), Gradient(gradient)) => {
|
||||||
Stroke::from_pair(gradient, thickness).into_value()
|
Stroke::from_pair(gradient, thickness).into_value()
|
||||||
}
|
}
|
||||||
(Pattern(pattern), Length(thickness)) | (Length(thickness), Pattern(pattern)) => {
|
(Tiling(tiling), Length(thickness)) | (Length(thickness), Tiling(tiling)) => {
|
||||||
Stroke::from_pair(pattern, thickness).into_value()
|
Stroke::from_pair(tiling, thickness).into_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
(Duration(a), Duration(b)) => Duration(a + b),
|
(Duration(a), Duration(b)) => Duration(a + b),
|
||||||
(Datetime(a), Duration(b)) => Datetime(a + b),
|
(Datetime(a), Duration(b)) => Datetime(a + b),
|
||||||
(Duration(a), Datetime(b)) => Datetime(b + a),
|
(Duration(a), Datetime(b)) => Datetime(b + a),
|
||||||
|
|
||||||
// Type compatibility.
|
|
||||||
(Type(a), Str(b)) => Str(format_str!("{a}{b}")),
|
|
||||||
(Str(a), Type(b)) => Str(format_str!("{a}{b}")),
|
|
||||||
|
|
||||||
(Dyn(a), Dyn(b)) => {
|
(Dyn(a), Dyn(b)) => {
|
||||||
// Alignments can be summed.
|
// Alignments can be summed.
|
||||||
if let (Some(&a), Some(&b)) =
|
if let (Some(&a), Some(&b)) =
|
||||||
@ -469,9 +460,6 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
|||||||
rat == rel.rel && rel.abs.is_zero()
|
rat == rel.rel && rel.abs.is_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type compatibility.
|
|
||||||
(Type(ty), Str(str)) | (Str(str), Type(ty)) => ty.compat_name() == str.as_str(),
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,10 +557,6 @@ pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
|
|||||||
(Str(a), Dict(b)) => Some(b.contains(a)),
|
(Str(a), Dict(b)) => Some(b.contains(a)),
|
||||||
(a, Array(b)) => Some(b.contains(a.clone())),
|
(a, Array(b)) => Some(b.contains(a.clone())),
|
||||||
|
|
||||||
// Type compatibility.
|
|
||||||
(Type(a), Str(b)) => Some(b.as_str().contains(a.compat_name())),
|
|
||||||
(Type(a), Dict(b)) => Some(b.contains(a.compat_name())),
|
|
||||||
|
|
||||||
_ => Option::None,
|
_ => Option::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,75 +3,19 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use comemo::{Track, Tracked};
|
use comemo::Tracked;
|
||||||
use ecow::{eco_vec, EcoString, EcoVec};
|
use ecow::{eco_vec, EcoString, EcoVec};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
use typst_utils::LazyHash;
|
use typst_utils::LazyHash;
|
||||||
|
|
||||||
use crate::diag::{warning, SourceResult, Trace, Tracepoint};
|
use crate::diag::{SourceResult, Trace, Tracepoint};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, func, ty, Content, Context, Element, Func, NativeElement, Packed, Repr,
|
cast, ty, Content, Context, Element, Func, NativeElement, Repr, Selector,
|
||||||
Selector, Show,
|
|
||||||
};
|
};
|
||||||
use crate::introspection::Locatable;
|
|
||||||
use crate::text::{FontFamily, FontList, TextElem};
|
use crate::text::{FontFamily, FontList, TextElem};
|
||||||
|
|
||||||
/// Provides access to active styles.
|
|
||||||
///
|
|
||||||
/// **Deprecation planned.** Use [context] instead.
|
|
||||||
///
|
|
||||||
/// ```example
|
|
||||||
/// #let thing(body) = style(styles => {
|
|
||||||
/// let size = measure(body, styles)
|
|
||||||
/// [Width of "#body" is #size.width]
|
|
||||||
/// })
|
|
||||||
///
|
|
||||||
/// #thing[Hey] \
|
|
||||||
/// #thing[Welcome]
|
|
||||||
/// ```
|
|
||||||
#[func]
|
|
||||||
pub fn style(
|
|
||||||
/// The engine.
|
|
||||||
engine: &mut Engine,
|
|
||||||
/// The call site span.
|
|
||||||
span: Span,
|
|
||||||
/// A function to call with the styles. Its return value is displayed
|
|
||||||
/// in the document.
|
|
||||||
///
|
|
||||||
/// This function is called once for each time the content returned by
|
|
||||||
/// `style` appears in the document. That makes it possible to generate
|
|
||||||
/// content that depends on the style context it appears in.
|
|
||||||
func: Func,
|
|
||||||
) -> Content {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "`style` is deprecated";
|
|
||||||
hint: "use a `context` expression instead"
|
|
||||||
));
|
|
||||||
|
|
||||||
StyleElem::new(func).pack().spanned(span)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes a style access.
|
|
||||||
#[elem(Locatable, Show)]
|
|
||||||
struct StyleElem {
|
|
||||||
/// The function to call with the styles.
|
|
||||||
#[required]
|
|
||||||
func: Func,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for Packed<StyleElem> {
|
|
||||||
#[typst_macros::time(name = "style", span = self.span())]
|
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
|
||||||
let context = Context::new(self.location(), Some(styles));
|
|
||||||
Ok(self
|
|
||||||
.func()
|
|
||||||
.call(engine, context.track(), [styles.to_map()])?
|
|
||||||
.display())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A list of style properties.
|
/// A list of style properties.
|
||||||
#[ty(cast)]
|
#[ty(cast)]
|
||||||
#[derive(Default, PartialEq, Clone, Hash)]
|
#[derive(Default, PartialEq, Clone, Hash)]
|
||||||
|
@ -246,8 +246,48 @@ impl Debug for List {
|
|||||||
|
|
||||||
impl crate::foundations::Repr for Symbol {
|
impl crate::foundations::Repr for Symbol {
|
||||||
fn repr(&self) -> EcoString {
|
fn repr(&self) -> EcoString {
|
||||||
eco_format!("\"{}\"", self.get())
|
match &self.0 {
|
||||||
|
Repr::Single(c) => eco_format!("symbol(\"{}\")", *c),
|
||||||
|
Repr::Complex(variants) => {
|
||||||
|
eco_format!("symbol{}", repr_variants(variants.iter().copied(), ""))
|
||||||
}
|
}
|
||||||
|
Repr::Modified(arc) => {
|
||||||
|
let (list, modifiers) = arc.as_ref();
|
||||||
|
if modifiers.is_empty() {
|
||||||
|
eco_format!("symbol{}", repr_variants(list.variants(), ""))
|
||||||
|
} else {
|
||||||
|
eco_format!("symbol{}", repr_variants(list.variants(), modifiers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn repr_variants<'a>(
|
||||||
|
variants: impl Iterator<Item = (&'a str, char)>,
|
||||||
|
applied_modifiers: &str,
|
||||||
|
) -> String {
|
||||||
|
crate::foundations::repr::pretty_array_like(
|
||||||
|
&variants
|
||||||
|
.filter(|(variant, _)| {
|
||||||
|
// Only keep variants that can still be accessed, i.e., variants
|
||||||
|
// that contain all applied modifiers.
|
||||||
|
parts(applied_modifiers).all(|am| variant.split('.').any(|m| m == am))
|
||||||
|
})
|
||||||
|
.map(|(variant, c)| {
|
||||||
|
let trimmed_variant = variant
|
||||||
|
.split('.')
|
||||||
|
.filter(|&m| parts(applied_modifiers).all(|am| m != am));
|
||||||
|
if trimmed_variant.clone().all(|m| m.is_empty()) {
|
||||||
|
eco_format!("\"{c}\"")
|
||||||
|
} else {
|
||||||
|
let trimmed_modifiers = trimmed_variant.collect::<Vec<_>>().join(".");
|
||||||
|
eco_format!("(\"{}\", \"{}\")", trimmed_modifiers, c)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Symbol {
|
impl Serialize for Symbol {
|
||||||
|
@ -44,16 +44,6 @@ use crate::foundations::{
|
|||||||
/// #type(int) \
|
/// #type(int) \
|
||||||
/// #type(type)
|
/// #type(type)
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// # Compatibility
|
|
||||||
/// In Typst 0.7 and lower, the `type` function returned a string instead of a
|
|
||||||
/// type. Compatibility with the old way will remain for a while to give package
|
|
||||||
/// authors time to upgrade, but it will be removed at some point.
|
|
||||||
///
|
|
||||||
/// - Checks like `{int == "integer"}` evaluate to `{true}`
|
|
||||||
/// - Adding/joining a type and string will yield a string
|
|
||||||
/// - The `{in}` operator on a type and a dictionary will evaluate to `{true}`
|
|
||||||
/// if the dictionary has a string key matching the type's name
|
|
||||||
#[ty(scope, cast)]
|
#[ty(scope, cast)]
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Type(Static<NativeTypeData>);
|
pub struct Type(Static<NativeTypeData>);
|
||||||
@ -111,14 +101,6 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type compatibility.
|
|
||||||
impl Type {
|
|
||||||
/// The type's backward-compatible name.
|
|
||||||
pub fn compat_name(&self) -> &str {
|
|
||||||
self.long_name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
impl Type {
|
impl Type {
|
||||||
/// Determines a value's type.
|
/// Determines a value's type.
|
||||||
|
@ -20,7 +20,7 @@ use crate::foundations::{
|
|||||||
};
|
};
|
||||||
use crate::layout::{Abs, Angle, Em, Fr, Length, Ratio, Rel};
|
use crate::layout::{Abs, Angle, Em, Fr, Length, Ratio, Rel};
|
||||||
use crate::text::{RawContent, RawElem, TextElem};
|
use crate::text::{RawContent, RawElem, TextElem};
|
||||||
use crate::visualize::{Color, Gradient, Pattern};
|
use crate::visualize::{Color, Gradient, Tiling};
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@ -50,8 +50,8 @@ pub enum Value {
|
|||||||
Color(Color),
|
Color(Color),
|
||||||
/// A gradient value: `gradient.linear(...)`.
|
/// A gradient value: `gradient.linear(...)`.
|
||||||
Gradient(Gradient),
|
Gradient(Gradient),
|
||||||
/// A pattern fill: `pattern(...)`.
|
/// A tiling fill: `tiling(...)`.
|
||||||
Pattern(Pattern),
|
Tiling(Tiling),
|
||||||
/// A symbol: `arrow.l`.
|
/// A symbol: `arrow.l`.
|
||||||
Symbol(Symbol),
|
Symbol(Symbol),
|
||||||
/// A version.
|
/// A version.
|
||||||
@ -130,7 +130,7 @@ impl Value {
|
|||||||
Self::Fraction(_) => Type::of::<Fr>(),
|
Self::Fraction(_) => Type::of::<Fr>(),
|
||||||
Self::Color(_) => Type::of::<Color>(),
|
Self::Color(_) => Type::of::<Color>(),
|
||||||
Self::Gradient(_) => Type::of::<Gradient>(),
|
Self::Gradient(_) => Type::of::<Gradient>(),
|
||||||
Self::Pattern(_) => Type::of::<Pattern>(),
|
Self::Tiling(_) => Type::of::<Tiling>(),
|
||||||
Self::Symbol(_) => Type::of::<Symbol>(),
|
Self::Symbol(_) => Type::of::<Symbol>(),
|
||||||
Self::Version(_) => Type::of::<Version>(),
|
Self::Version(_) => Type::of::<Version>(),
|
||||||
Self::Str(_) => Type::of::<Str>(),
|
Self::Str(_) => Type::of::<Str>(),
|
||||||
@ -244,7 +244,7 @@ impl Debug for Value {
|
|||||||
Self::Fraction(v) => Debug::fmt(v, f),
|
Self::Fraction(v) => Debug::fmt(v, f),
|
||||||
Self::Color(v) => Debug::fmt(v, f),
|
Self::Color(v) => Debug::fmt(v, f),
|
||||||
Self::Gradient(v) => Debug::fmt(v, f),
|
Self::Gradient(v) => Debug::fmt(v, f),
|
||||||
Self::Pattern(v) => Debug::fmt(v, f),
|
Self::Tiling(v) => Debug::fmt(v, f),
|
||||||
Self::Symbol(v) => Debug::fmt(v, f),
|
Self::Symbol(v) => Debug::fmt(v, f),
|
||||||
Self::Version(v) => Debug::fmt(v, f),
|
Self::Version(v) => Debug::fmt(v, f),
|
||||||
Self::Str(v) => Debug::fmt(v, f),
|
Self::Str(v) => Debug::fmt(v, f),
|
||||||
@ -282,7 +282,7 @@ impl Repr for Value {
|
|||||||
Self::Fraction(v) => v.repr(),
|
Self::Fraction(v) => v.repr(),
|
||||||
Self::Color(v) => v.repr(),
|
Self::Color(v) => v.repr(),
|
||||||
Self::Gradient(v) => v.repr(),
|
Self::Gradient(v) => v.repr(),
|
||||||
Self::Pattern(v) => v.repr(),
|
Self::Tiling(v) => v.repr(),
|
||||||
Self::Symbol(v) => v.repr(),
|
Self::Symbol(v) => v.repr(),
|
||||||
Self::Version(v) => v.repr(),
|
Self::Version(v) => v.repr(),
|
||||||
Self::Str(v) => v.repr(),
|
Self::Str(v) => v.repr(),
|
||||||
@ -333,7 +333,7 @@ impl Hash for Value {
|
|||||||
Self::Fraction(v) => v.hash(state),
|
Self::Fraction(v) => v.hash(state),
|
||||||
Self::Color(v) => v.hash(state),
|
Self::Color(v) => v.hash(state),
|
||||||
Self::Gradient(v) => v.hash(state),
|
Self::Gradient(v) => v.hash(state),
|
||||||
Self::Pattern(v) => v.hash(state),
|
Self::Tiling(v) => v.hash(state),
|
||||||
Self::Symbol(v) => v.hash(state),
|
Self::Symbol(v) => v.hash(state),
|
||||||
Self::Version(v) => v.hash(state),
|
Self::Version(v) => v.hash(state),
|
||||||
Self::Str(v) => v.hash(state),
|
Self::Str(v) => v.hash(state),
|
||||||
@ -640,7 +640,7 @@ primitive! { Rel<Length>: "relative length",
|
|||||||
primitive! { Fr: "fraction", Fraction }
|
primitive! { Fr: "fraction", Fraction }
|
||||||
primitive! { Color: "color", Color }
|
primitive! { Color: "color", Color }
|
||||||
primitive! { Gradient: "gradient", Gradient }
|
primitive! { Gradient: "gradient", Gradient }
|
||||||
primitive! { Pattern: "pattern", Pattern }
|
primitive! { Tiling: "tiling", Tiling }
|
||||||
primitive! { Symbol: "symbol", Symbol }
|
primitive! { Symbol: "symbol", Symbol }
|
||||||
primitive! { Version: "version", Version }
|
primitive! { Version: "version", Version }
|
||||||
primitive! {
|
primitive! {
|
||||||
|
@ -122,8 +122,8 @@ impl HtmlTag {
|
|||||||
let bytes = string.as_bytes();
|
let bytes = string.as_bytes();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < bytes.len() {
|
while i < bytes.len() {
|
||||||
if !bytes[i].is_ascii_alphanumeric() {
|
if !bytes[i].is_ascii() || !charsets::is_valid_in_tag_name(bytes[i] as char) {
|
||||||
panic!("constant tag name must be ASCII alphanumeric");
|
panic!("not all characters are valid in a tag name");
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
@ -220,8 +220,10 @@ impl HtmlAttr {
|
|||||||
let bytes = string.as_bytes();
|
let bytes = string.as_bytes();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < bytes.len() {
|
while i < bytes.len() {
|
||||||
if !bytes[i].is_ascii_alphanumeric() {
|
if !bytes[i].is_ascii()
|
||||||
panic!("constant attribute name must be ASCII alphanumeric");
|
|| !charsets::is_valid_in_attribute_name(bytes[i] as char)
|
||||||
|
{
|
||||||
|
panic!("not all characters are valid in an attribute name");
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
@ -621,5 +623,9 @@ pub mod attr {
|
|||||||
href
|
href
|
||||||
name
|
name
|
||||||
value
|
value
|
||||||
|
role
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub const aria_level: HtmlAttr = HtmlAttr::constant("aria-level");
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use smallvec::{smallvec, SmallVec};
|
|||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
|
|
||||||
use crate::diag::{bail, warning, At, HintedStrResult, SourceResult};
|
use crate::diag::{bail, At, HintedStrResult, SourceResult};
|
||||||
use crate::engine::{Engine, Route, Sink, Traced};
|
use crate::engine::{Engine, Route, Sink, Traced};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, func, scope, select_where, ty, Args, Array, Construct, Content, Context,
|
cast, elem, func, scope, select_where, ty, Args, Array, Construct, Content, Context,
|
||||||
@ -353,7 +353,7 @@ impl Counter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shared implementation of displaying between `counter.display` and
|
/// Shared implementation of displaying between `counter.display` and
|
||||||
/// `DisplayElem`, which will be deprecated.
|
/// `CounterDisplayElem`.
|
||||||
fn display_impl(
|
fn display_impl(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
@ -366,10 +366,9 @@ impl Counter {
|
|||||||
.custom()
|
.custom()
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let styles = styles?;
|
let styles = styles?;
|
||||||
let CounterKey::Selector(Selector::Elem(func, _)) = self.0 else {
|
match self.0 {
|
||||||
return None;
|
CounterKey::Page => PageElem::numbering_in(styles).clone(),
|
||||||
};
|
CounterKey::Selector(Selector::Elem(func, _)) => {
|
||||||
|
|
||||||
if func == HeadingElem::elem() {
|
if func == HeadingElem::elem() {
|
||||||
HeadingElem::numbering_in(styles).clone()
|
HeadingElem::numbering_in(styles).clone()
|
||||||
} else if func == FigureElem::elem() {
|
} else if func == FigureElem::elem() {
|
||||||
@ -381,6 +380,9 @@ impl Counter {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into());
|
.unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into());
|
||||||
|
|
||||||
@ -439,11 +441,6 @@ impl Counter {
|
|||||||
|
|
||||||
/// Displays the current value of the counter with a numbering and returns
|
/// Displays the current value of the counter with a numbering and returns
|
||||||
/// the formatted output.
|
/// the formatted output.
|
||||||
///
|
|
||||||
/// _Compatibility:_ For compatibility with Typst 0.10 and lower, this
|
|
||||||
/// function also works without an established context. Then, it will create
|
|
||||||
/// opaque contextual content rather than directly returning the output of
|
|
||||||
/// the numbering. This behaviour will be removed in a future release.
|
|
||||||
#[func(contextual)]
|
#[func(contextual)]
|
||||||
pub fn display(
|
pub fn display(
|
||||||
self,
|
self,
|
||||||
@ -472,19 +469,8 @@ impl Counter {
|
|||||||
#[default(false)]
|
#[default(false)]
|
||||||
both: bool,
|
both: bool,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
if let Ok(loc) = context.location() {
|
let loc = context.location().at(span)?;
|
||||||
self.display_impl(engine, loc, numbering, both, context.styles().ok())
|
self.display_impl(engine, loc, numbering, both, context.styles().ok())
|
||||||
} else {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "`counter.display` without context is deprecated";
|
|
||||||
hint: "use it in a `context` expression instead"
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(CounterDisplayElem::new(self, numbering, both)
|
|
||||||
.pack()
|
|
||||||
.spanned(span)
|
|
||||||
.into_value())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the value of the counter at the given location. Always returns
|
/// Retrieves the value of the counter at the given location. Always returns
|
||||||
@ -493,10 +479,6 @@ impl Counter {
|
|||||||
/// The `selector` must match exactly one element in the document. The most
|
/// The `selector` must match exactly one element in the document. The most
|
||||||
/// useful kinds of selectors for this are [labels]($label) and
|
/// useful kinds of selectors for this are [labels]($label) and
|
||||||
/// [locations]($location).
|
/// [locations]($location).
|
||||||
///
|
|
||||||
/// _Compatibility:_ For compatibility with Typst 0.10 and lower, this
|
|
||||||
/// function also works without a known context if the `selector` is a
|
|
||||||
/// location. This behaviour will be removed in a future release.
|
|
||||||
#[func(contextual)]
|
#[func(contextual)]
|
||||||
pub fn at(
|
pub fn at(
|
||||||
&self,
|
&self,
|
||||||
@ -524,21 +506,8 @@ impl Counter {
|
|||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
/// The callsite span.
|
/// The callsite span.
|
||||||
span: Span,
|
span: Span,
|
||||||
/// _Compatibility:_ This argument is deprecated. It only exists for
|
|
||||||
/// compatibility with Typst 0.10 and lower and shouldn't be used
|
|
||||||
/// anymore.
|
|
||||||
#[default]
|
|
||||||
location: Option<Location>,
|
|
||||||
) -> SourceResult<CounterState> {
|
) -> SourceResult<CounterState> {
|
||||||
if location.is_none() {
|
context.introspect().at(span)?;
|
||||||
context.location().at(span)?;
|
|
||||||
} else {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "calling `counter.final` with a location is deprecated";
|
|
||||||
hint: "try removing the location argument"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let sequence = self.sequence(engine)?;
|
let sequence = self.sequence(engine)?;
|
||||||
let (mut state, page) = sequence.last().unwrap().clone();
|
let (mut state, page) = sequence.last().unwrap().clone();
|
||||||
if self.is_page() {
|
if self.is_page() {
|
||||||
@ -759,8 +728,6 @@ impl Count for Packed<CounterUpdateElem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a display of a counter.
|
/// Executes a display of a counter.
|
||||||
///
|
|
||||||
/// **Deprecation planned.**
|
|
||||||
#[elem(Construct, Locatable, Show)]
|
#[elem(Construct, Locatable, Show)]
|
||||||
pub struct CounterDisplayElem {
|
pub struct CounterDisplayElem {
|
||||||
/// The counter.
|
/// The counter.
|
||||||
@ -786,7 +753,6 @@ impl Construct for CounterDisplayElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for Packed<CounterDisplayElem> {
|
impl Show for Packed<CounterDisplayElem> {
|
||||||
#[typst_macros::time(name = "counter.display", span = self.span())]
|
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.counter
|
.counter
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use comemo::{Track, Tracked};
|
use comemo::Tracked;
|
||||||
use typst_syntax::Span;
|
|
||||||
|
|
||||||
use crate::diag::{warning, HintedStrResult, SourceResult};
|
use crate::diag::HintedStrResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{func, Context, LocatableSelector};
|
||||||
cast, elem, func, Content, Context, Func, LocatableSelector, NativeElement, Packed,
|
use crate::introspection::Location;
|
||||||
Show, StyleChain, Value,
|
|
||||||
};
|
|
||||||
use crate::introspection::{Locatable, Location};
|
|
||||||
|
|
||||||
/// Determines the location of an element in the document.
|
/// Determines the location of an element in the document.
|
||||||
///
|
///
|
||||||
@ -26,23 +22,12 @@ use crate::introspection::{Locatable, Location};
|
|||||||
///
|
///
|
||||||
/// = Introduction <intro>
|
/// = Introduction <intro>
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// # Compatibility
|
|
||||||
/// In Typst 0.10 and lower, the `locate` function took a closure that made the
|
|
||||||
/// current location in the document available (like [`here`] does now). This
|
|
||||||
/// usage pattern is deprecated. Compatibility with the old way will remain for
|
|
||||||
/// a while to give package authors time to upgrade. To that effect, `locate`
|
|
||||||
/// detects whether it received a selector or a user-defined function and
|
|
||||||
/// adjusts its semantics accordingly. This behaviour will be removed in the
|
|
||||||
/// future.
|
|
||||||
#[func(contextual)]
|
#[func(contextual)]
|
||||||
pub fn locate(
|
pub fn locate(
|
||||||
/// The engine.
|
/// The engine.
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// The callsite context.
|
/// The callsite context.
|
||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
/// The span of the `locate` call.
|
|
||||||
span: Span,
|
|
||||||
/// A selector that should match exactly one element. This element will be
|
/// A selector that should match exactly one element. This element will be
|
||||||
/// located.
|
/// located.
|
||||||
///
|
///
|
||||||
@ -50,70 +35,7 @@ pub fn locate(
|
|||||||
/// - [`here`] to locate the current context,
|
/// - [`here`] to locate the current context,
|
||||||
/// - a [`location`] retrieved from some queried element via the
|
/// - a [`location`] retrieved from some queried element via the
|
||||||
/// [`location()`]($content.location) method on content.
|
/// [`location()`]($content.location) method on content.
|
||||||
selector: LocateInput,
|
selector: LocatableSelector,
|
||||||
) -> HintedStrResult<LocateOutput> {
|
) -> HintedStrResult<Location> {
|
||||||
Ok(match selector {
|
selector.resolve_unique(engine.introspector, context)
|
||||||
LocateInput::Selector(selector) => {
|
|
||||||
LocateOutput::Location(selector.resolve_unique(engine.introspector, context)?)
|
|
||||||
}
|
|
||||||
LocateInput::Func(func) => {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "`locate` with callback function is deprecated";
|
|
||||||
hint: "use a `context` expression instead"
|
|
||||||
));
|
|
||||||
|
|
||||||
LocateOutput::Content(LocateElem::new(func).pack().spanned(span))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compatible input type.
|
|
||||||
pub enum LocateInput {
|
|
||||||
Selector(LocatableSelector),
|
|
||||||
Func(Func),
|
|
||||||
}
|
|
||||||
|
|
||||||
cast! {
|
|
||||||
LocateInput,
|
|
||||||
v: Func => {
|
|
||||||
if v.element().is_some() {
|
|
||||||
Self::Selector(Value::Func(v).cast()?)
|
|
||||||
} else {
|
|
||||||
Self::Func(v)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
v: LocatableSelector => Self::Selector(v),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compatible output type.
|
|
||||||
pub enum LocateOutput {
|
|
||||||
Location(Location),
|
|
||||||
Content(Content),
|
|
||||||
}
|
|
||||||
|
|
||||||
cast! {
|
|
||||||
LocateOutput,
|
|
||||||
self => match self {
|
|
||||||
Self::Location(v) => v.into_value(),
|
|
||||||
Self::Content(v) => v.into_value(),
|
|
||||||
},
|
|
||||||
v: Location => Self::Location(v),
|
|
||||||
v: Content => Self::Content(v),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes a `locate` call.
|
|
||||||
#[elem(Locatable, Show)]
|
|
||||||
struct LocateElem {
|
|
||||||
/// The function to call with the location.
|
|
||||||
#[required]
|
|
||||||
func: Func,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for Packed<LocateElem> {
|
|
||||||
#[typst_macros::time(name = "locate", span = self.span())]
|
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
|
||||||
let location = self.location().unwrap();
|
|
||||||
let context = Context::new(Some(location), Some(styles));
|
|
||||||
Ok(self.func().call(engine, context.track(), [location])?.display())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ impl<'a> Locator<'a> {
|
|||||||
///
|
///
|
||||||
/// Should typically only be created at the document level, though there
|
/// Should typically only be created at the document level, though there
|
||||||
/// are a few places where we use it as well that just don't support
|
/// are a few places where we use it as well that just don't support
|
||||||
/// introspection (e.g. drawable patterns).
|
/// introspection (e.g. tilings).
|
||||||
pub fn root() -> Self {
|
pub fn root() -> Self {
|
||||||
Self { local: 0, outer: None }
|
Self { local: 0, outer: None }
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
use typst_syntax::Span;
|
|
||||||
|
|
||||||
use crate::diag::{warning, HintedStrResult};
|
use crate::diag::HintedStrResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, Array, Context, LocatableSelector, Value};
|
use crate::foundations::{func, Array, Context, LocatableSelector, Value};
|
||||||
use crate::introspection::Location;
|
|
||||||
|
|
||||||
/// Finds elements in the document.
|
/// Finds elements in the document.
|
||||||
///
|
///
|
||||||
@ -142,8 +140,6 @@ pub fn query(
|
|||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// The callsite context.
|
/// The callsite context.
|
||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
/// The span of the `query` call.
|
|
||||||
span: Span,
|
|
||||||
/// Can be
|
/// Can be
|
||||||
/// - an element function like a `heading` or `figure`,
|
/// - an element function like a `heading` or `figure`,
|
||||||
/// - a `{<label>}`,
|
/// - a `{<label>}`,
|
||||||
@ -152,20 +148,8 @@ pub fn query(
|
|||||||
///
|
///
|
||||||
/// Only [locatable]($location/#locatable) element functions are supported.
|
/// Only [locatable]($location/#locatable) element functions are supported.
|
||||||
target: LocatableSelector,
|
target: LocatableSelector,
|
||||||
/// _Compatibility:_ This argument is deprecated. It only exists for
|
|
||||||
/// compatibility with Typst 0.10 and lower and shouldn't be used anymore.
|
|
||||||
#[default]
|
|
||||||
location: Option<Location>,
|
|
||||||
) -> HintedStrResult<Array> {
|
) -> HintedStrResult<Array> {
|
||||||
if location.is_none() {
|
|
||||||
context.introspect()?;
|
context.introspect()?;
|
||||||
} else {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "calling `query` with a location is deprecated";
|
|
||||||
hint: "try removing the location argument"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let vec = engine.introspector.query(&target.0);
|
let vec = engine.introspector.query(&target.0);
|
||||||
Ok(vec.into_iter().map(Value::Content).collect())
|
Ok(vec.into_iter().map(Value::Content).collect())
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use comemo::{Track, Tracked, TrackedMut};
|
|||||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
|
|
||||||
use crate::diag::{bail, warning, At, SourceResult};
|
use crate::diag::{bail, At, SourceResult};
|
||||||
use crate::engine::{Engine, Route, Sink, Traced};
|
use crate::engine::{Engine, Route, Sink, Traced};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, func, scope, select_where, ty, Args, Construct, Content, Context, Func,
|
cast, elem, func, scope, select_where, ty, Args, Construct, Content, Context, Func,
|
||||||
@ -305,10 +305,6 @@ impl State {
|
|||||||
/// The `selector` must match exactly one element in the document. The most
|
/// The `selector` must match exactly one element in the document. The most
|
||||||
/// useful kinds of selectors for this are [labels]($label) and
|
/// useful kinds of selectors for this are [labels]($label) and
|
||||||
/// [locations]($location).
|
/// [locations]($location).
|
||||||
///
|
|
||||||
/// _Compatibility:_ For compatibility with Typst 0.10 and lower, this
|
|
||||||
/// function also works without a known context if the `selector` is a
|
|
||||||
/// location. This behaviour will be removed in a future release.
|
|
||||||
#[typst_macros::time(name = "state.at", span = span)]
|
#[typst_macros::time(name = "state.at", span = span)]
|
||||||
#[func(contextual)]
|
#[func(contextual)]
|
||||||
pub fn at(
|
pub fn at(
|
||||||
@ -336,21 +332,8 @@ impl State {
|
|||||||
context: Tracked<Context>,
|
context: Tracked<Context>,
|
||||||
/// The callsite span.
|
/// The callsite span.
|
||||||
span: Span,
|
span: Span,
|
||||||
/// _Compatibility:_ This argument is deprecated. It only exists for
|
|
||||||
/// compatibility with Typst 0.10 and lower and shouldn't be used
|
|
||||||
/// anymore.
|
|
||||||
#[default]
|
|
||||||
location: Option<Location>,
|
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
if location.is_none() {
|
context.introspect().at(span)?;
|
||||||
context.location().at(span)?;
|
|
||||||
} else {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "calling `state.final` with a location is deprecated";
|
|
||||||
hint: "try removing the location argument"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let sequence = self.sequence(engine)?;
|
let sequence = self.sequence(engine)?;
|
||||||
Ok(sequence.last().unwrap().clone())
|
Ok(sequence.last().unwrap().clone())
|
||||||
}
|
}
|
||||||
@ -375,30 +358,6 @@ impl State {
|
|||||||
) -> Content {
|
) -> Content {
|
||||||
StateUpdateElem::new(self.key, update).pack().spanned(span)
|
StateUpdateElem::new(self.key, update).pack().spanned(span)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays the current value of the state.
|
|
||||||
///
|
|
||||||
/// **Deprecation planned:** Use [`get`]($state.get) instead.
|
|
||||||
#[func]
|
|
||||||
pub fn display(
|
|
||||||
self,
|
|
||||||
/// The engine.
|
|
||||||
engine: &mut Engine,
|
|
||||||
/// The span of the `display` call.
|
|
||||||
span: Span,
|
|
||||||
/// A function which receives the value of the state and can return
|
|
||||||
/// arbitrary content which is then displayed. If this is omitted, the
|
|
||||||
/// value is directly displayed.
|
|
||||||
#[default]
|
|
||||||
func: Option<Func>,
|
|
||||||
) -> Content {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "`state.display` is deprecated";
|
|
||||||
hint: "use `state.get` in a `context` expression instead"
|
|
||||||
));
|
|
||||||
|
|
||||||
StateDisplayElem::new(self, func).pack().spanned(span)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repr for State {
|
impl Repr for State {
|
||||||
@ -446,38 +405,3 @@ impl Show for Packed<StateUpdateElem> {
|
|||||||
Ok(Content::empty())
|
Ok(Content::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a display of a state.
|
|
||||||
///
|
|
||||||
/// **Deprecation planned.**
|
|
||||||
#[elem(Construct, Locatable, Show)]
|
|
||||||
struct StateDisplayElem {
|
|
||||||
/// The state.
|
|
||||||
#[required]
|
|
||||||
#[internal]
|
|
||||||
state: State,
|
|
||||||
|
|
||||||
/// The function to display the state with.
|
|
||||||
#[required]
|
|
||||||
#[internal]
|
|
||||||
func: Option<Func>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Show for Packed<StateDisplayElem> {
|
|
||||||
#[typst_macros::time(name = "state.display", span = self.span())]
|
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
|
||||||
let location = self.location().unwrap();
|
|
||||||
let context = Context::new(Some(location), Some(styles));
|
|
||||||
let value = self.state().at_loc(engine, location)?;
|
|
||||||
Ok(match self.func() {
|
|
||||||
Some(func) => func.call(engine, context.track(), [value])?.display(),
|
|
||||||
None => value.display(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Construct for StateDisplayElem {
|
|
||||||
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
bail!(args.span, "cannot be constructed manually");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
|
|
||||||
use crate::diag::{warning, At, SourceResult};
|
use crate::diag::{At, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{dict, func, Content, Context, Dict, Resolve, Smart};
|
||||||
dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles,
|
|
||||||
};
|
|
||||||
use crate::introspection::{Locator, LocatorLink};
|
use crate::introspection::{Locator, LocatorLink};
|
||||||
use crate::layout::{Abs, Axes, Length, Region, Size};
|
use crate::layout::{Abs, Axes, Length, Region, Size};
|
||||||
|
|
||||||
@ -76,23 +74,9 @@ pub fn measure(
|
|||||||
height: Smart<Length>,
|
height: Smart<Length>,
|
||||||
/// The content whose size to measure.
|
/// The content whose size to measure.
|
||||||
content: Content,
|
content: Content,
|
||||||
/// _Compatibility:_ This argument is deprecated. It only exists for
|
|
||||||
/// compatibility with Typst 0.10 and lower and shouldn't be used anymore.
|
|
||||||
#[default]
|
|
||||||
styles: Option<Styles>,
|
|
||||||
) -> SourceResult<Dict> {
|
) -> SourceResult<Dict> {
|
||||||
let styles = match &styles {
|
|
||||||
Some(styles) => {
|
|
||||||
engine.sink.warn(warning!(
|
|
||||||
span, "calling `measure` with a styles argument is deprecated";
|
|
||||||
hint: "try removing the styles argument"
|
|
||||||
));
|
|
||||||
StyleChain::new(styles)
|
|
||||||
}
|
|
||||||
None => context.styles().at(span)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a pod region with the available space.
|
// Create a pod region with the available space.
|
||||||
|
let styles = context.styles().at(span)?;
|
||||||
let pod = Region::new(
|
let pod = Region::new(
|
||||||
Axes::new(
|
Axes::new(
|
||||||
width.resolve(styles).unwrap_or(Abs::inf()),
|
width.resolve(styles).unwrap_or(Abs::inf()),
|
||||||
|
@ -9,7 +9,7 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Array, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
cast, elem, scope, Array, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
||||||
Styles, TargetElem,
|
Styles, TargetElem,
|
||||||
};
|
};
|
||||||
use crate::html::{attr, tag, HtmlElem};
|
use crate::html::{attr, tag, HtmlAttr, HtmlElem};
|
||||||
use crate::layout::{Alignment, BlockElem, Em, HAlignment, Length, VAlignment, VElem};
|
use crate::layout::{Alignment, BlockElem, Em, HAlignment, Length, VAlignment, VElem};
|
||||||
use crate::model::{ListItemLike, ListLike, Numbering, NumberingPattern, ParElem};
|
use crate::model::{ListItemLike, ListLike, Numbering, NumberingPattern, ParElem};
|
||||||
|
|
||||||
@ -127,8 +127,7 @@ pub struct EnumElem {
|
|||||||
/// [Ahead],
|
/// [Ahead],
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[default(1)]
|
pub start: Smart<usize>,
|
||||||
pub start: usize,
|
|
||||||
|
|
||||||
/// Whether to display the full numbering, including the numbers of
|
/// Whether to display the full numbering, including the numbers of
|
||||||
/// all parent enumerations.
|
/// all parent enumerations.
|
||||||
@ -144,6 +143,17 @@ pub struct EnumElem {
|
|||||||
#[default(false)]
|
#[default(false)]
|
||||||
pub full: bool,
|
pub full: bool,
|
||||||
|
|
||||||
|
/// Whether to reverse the numbering for this enumeration.
|
||||||
|
///
|
||||||
|
/// ```example
|
||||||
|
/// #set enum(reversed: true)
|
||||||
|
/// + Coffee
|
||||||
|
/// + Tea
|
||||||
|
/// + Milk
|
||||||
|
/// ```
|
||||||
|
#[default(false)]
|
||||||
|
pub reversed: bool,
|
||||||
|
|
||||||
/// The indentation of each item.
|
/// The indentation of each item.
|
||||||
#[resolve]
|
#[resolve]
|
||||||
pub indent: Length,
|
pub indent: Length,
|
||||||
@ -217,7 +227,12 @@ impl EnumElem {
|
|||||||
impl Show for Packed<EnumElem> {
|
impl Show for Packed<EnumElem> {
|
||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
if TargetElem::target_in(styles).is_html() {
|
if TargetElem::target_in(styles).is_html() {
|
||||||
return Ok(HtmlElem::new(tag::ol)
|
let mut elem = HtmlElem::new(tag::ol);
|
||||||
|
if self.reversed(styles) {
|
||||||
|
elem =
|
||||||
|
elem.with_attr(const { HtmlAttr::constant("reversed") }, "reversed");
|
||||||
|
}
|
||||||
|
return Ok(elem
|
||||||
.with_body(Some(Content::sequence(self.children.iter().map(|item| {
|
.with_body(Some(Content::sequence(self.children.iter().map(|item| {
|
||||||
let mut li = HtmlElem::new(tag::li);
|
let mut li = HtmlElem::new(tag::li);
|
||||||
if let Some(nr) = item.number(styles) {
|
if let Some(nr) = item.number(styles) {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
|
use ecow::eco_format;
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::{warning, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain,
|
||||||
Styles, Synthesize, TargetElem,
|
Styles, Synthesize, TargetElem,
|
||||||
};
|
};
|
||||||
use crate::html::{tag, HtmlElem};
|
use crate::html::{attr, tag, HtmlElem};
|
||||||
use crate::introspection::{
|
use crate::introspection::{
|
||||||
Count, Counter, CounterUpdate, Locatable, Locator, LocatorLink,
|
Count, Counter, CounterUpdate, Locatable, Locator, LocatorLink,
|
||||||
};
|
};
|
||||||
@ -272,9 +273,26 @@ impl Show for Packed<HeadingElem> {
|
|||||||
// Meanwhile, a level 1 Typst heading is a section heading. For this
|
// Meanwhile, a level 1 Typst heading is a section heading. For this
|
||||||
// reason, levels are offset by one: A Typst level 1 heading becomes
|
// reason, levels are offset by one: A Typst level 1 heading becomes
|
||||||
// a `<h2>`.
|
// a `<h2>`.
|
||||||
let level = self.resolve_level(styles);
|
let level = self.resolve_level(styles).get();
|
||||||
let t = [tag::h2, tag::h3, tag::h4, tag::h5, tag::h6][level.get().min(5) - 1];
|
if level >= 6 {
|
||||||
|
engine.sink.warn(warning!(span,
|
||||||
|
"heading of level {} was transformed to \
|
||||||
|
<div role=\"heading\" aria-level=\"{}\">, which is not \
|
||||||
|
supported by all assistive technology",
|
||||||
|
level, level + 1;
|
||||||
|
hint: "HTML only supports <h1> to <h6>, not <h{}>", level + 1;
|
||||||
|
hint: "you may want to restructure your document so that \
|
||||||
|
it doesn't contain deep headings"));
|
||||||
|
HtmlElem::new(tag::div)
|
||||||
|
.with_body(Some(realized))
|
||||||
|
.with_attr(attr::role, "heading")
|
||||||
|
.with_attr(attr::aria_level, eco_format!("{}", level + 1))
|
||||||
|
.pack()
|
||||||
|
.spanned(span)
|
||||||
|
} else {
|
||||||
|
let t = [tag::h2, tag::h3, tag::h4, tag::h5, tag::h6][level - 1];
|
||||||
HtmlElem::new(t).with_body(Some(realized)).pack().spanned(span)
|
HtmlElem::new(t).with_body(Some(realized)).pack().spanned(span)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let realized = BlockBody::Content(realized);
|
let realized = BlockBody::Content(realized);
|
||||||
BlockElem::new().with_body(Some(realized)).pack().spanned(span)
|
BlockElem::new().with_body(Some(realized)).pack().spanned(span)
|
||||||
|
@ -137,10 +137,6 @@ pub struct OutlineElem {
|
|||||||
/// `{n => n * 2em}` would be equivalent to just specifying `{2em}`, while
|
/// `{n => n * 2em}` would be equivalent to just specifying `{2em}`, while
|
||||||
/// `{n => [→ ] * n}` would indent with one arrow per nesting level.
|
/// `{n => [→ ] * n}` would indent with one arrow per nesting level.
|
||||||
///
|
///
|
||||||
/// *Migration hints:* Specifying `{true}` (equivalent to `{auto}`) or
|
|
||||||
/// `{false}` (equivalent to `{none}`) for this option is deprecated and
|
|
||||||
/// will be removed in a future release.
|
|
||||||
///
|
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #set heading(numbering: "1.a.")
|
/// #set heading(numbering: "1.a.")
|
||||||
///
|
///
|
||||||
@ -294,7 +290,6 @@ pub trait Outlinable: Refable {
|
|||||||
/// Defines how an outline is indented.
|
/// Defines how an outline is indented.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub enum OutlineIndent {
|
pub enum OutlineIndent {
|
||||||
Bool(bool),
|
|
||||||
Rel(Rel<Length>),
|
Rel(Rel<Length>),
|
||||||
Func(Func),
|
Func(Func),
|
||||||
}
|
}
|
||||||
@ -310,10 +305,10 @@ impl OutlineIndent {
|
|||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
match indent {
|
match indent {
|
||||||
// 'none' | 'false' => no indenting
|
// 'none' | 'false' => no indenting
|
||||||
None | Some(Smart::Custom(OutlineIndent::Bool(false))) => {}
|
None => {}
|
||||||
|
|
||||||
// 'auto' | 'true' => use numbering alignment for indenting
|
// 'auto' | 'true' => use numbering alignment for indenting
|
||||||
Some(Smart::Auto | Smart::Custom(OutlineIndent::Bool(true))) => {
|
Some(Smart::Auto) => {
|
||||||
// Add hidden ancestors numberings to realize the indent.
|
// Add hidden ancestors numberings to realize the indent.
|
||||||
let mut hidden = Content::empty();
|
let mut hidden = Content::empty();
|
||||||
for ancestor in ancestors {
|
for ancestor in ancestors {
|
||||||
@ -368,11 +363,9 @@ impl OutlineIndent {
|
|||||||
cast! {
|
cast! {
|
||||||
OutlineIndent,
|
OutlineIndent,
|
||||||
self => match self {
|
self => match self {
|
||||||
Self::Bool(v) => v.into_value(),
|
|
||||||
Self::Rel(v) => v.into_value(),
|
Self::Rel(v) => v.into_value(),
|
||||||
Self::Func(v) => v.into_value()
|
Self::Func(v) => v.into_value()
|
||||||
},
|
},
|
||||||
v: bool => OutlineIndent::Bool(v),
|
|
||||||
v: Rel<Length> => OutlineIndent::Rel(v),
|
v: Rel<Length> => OutlineIndent::Rel(v),
|
||||||
v: Func => OutlineIndent::Func(v),
|
v: Func => OutlineIndent::Func(v),
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ pub struct TextElem {
|
|||||||
if paint.v.relative() == Smart::Custom(RelativeTo::Self_) {
|
if paint.v.relative() == Smart::Custom(RelativeTo::Self_) {
|
||||||
bail!(
|
bail!(
|
||||||
paint.span,
|
paint.span,
|
||||||
"gradients and patterns on text must be relative to the parent";
|
"gradients and tilings on text must be relative to the parent";
|
||||||
hint: "make sure to set `relative: auto` on your text fill"
|
hint: "make sure to set `relative: auto` on your text fill"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@ mod image;
|
|||||||
mod line;
|
mod line;
|
||||||
mod paint;
|
mod paint;
|
||||||
mod path;
|
mod path;
|
||||||
mod pattern;
|
|
||||||
mod polygon;
|
mod polygon;
|
||||||
mod shape;
|
mod shape;
|
||||||
mod stroke;
|
mod stroke;
|
||||||
|
mod tiling;
|
||||||
|
|
||||||
pub use self::color::*;
|
pub use self::color::*;
|
||||||
pub use self::gradient::*;
|
pub use self::gradient::*;
|
||||||
@ -17,12 +17,12 @@ pub use self::image::*;
|
|||||||
pub use self::line::*;
|
pub use self::line::*;
|
||||||
pub use self::paint::*;
|
pub use self::paint::*;
|
||||||
pub use self::path::*;
|
pub use self::path::*;
|
||||||
pub use self::pattern::*;
|
|
||||||
pub use self::polygon::*;
|
pub use self::polygon::*;
|
||||||
pub use self::shape::*;
|
pub use self::shape::*;
|
||||||
pub use self::stroke::*;
|
pub use self::stroke::*;
|
||||||
|
pub use self::tiling::*;
|
||||||
|
|
||||||
use crate::foundations::{category, Category, Scope};
|
use crate::foundations::{category, Category, Scope, Type};
|
||||||
|
|
||||||
/// Drawing and data visualization.
|
/// Drawing and data visualization.
|
||||||
///
|
///
|
||||||
@ -37,7 +37,7 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
global.category(VISUALIZE);
|
global.category(VISUALIZE);
|
||||||
global.define_type::<Color>();
|
global.define_type::<Color>();
|
||||||
global.define_type::<Gradient>();
|
global.define_type::<Gradient>();
|
||||||
global.define_type::<Pattern>();
|
global.define_type::<Tiling>();
|
||||||
global.define_type::<Stroke>();
|
global.define_type::<Stroke>();
|
||||||
global.define_elem::<ImageElem>();
|
global.define_elem::<ImageElem>();
|
||||||
global.define_elem::<LineElem>();
|
global.define_elem::<LineElem>();
|
||||||
@ -47,4 +47,7 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
global.define_elem::<CircleElem>();
|
global.define_elem::<CircleElem>();
|
||||||
global.define_elem::<PolygonElem>();
|
global.define_elem::<PolygonElem>();
|
||||||
global.define_elem::<PathElem>();
|
global.define_elem::<PathElem>();
|
||||||
|
|
||||||
|
// Compatibility.
|
||||||
|
global.define("pattern", Type::of::<Tiling>());
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
|
|
||||||
use crate::foundations::{cast, Repr, Smart};
|
use crate::foundations::{cast, Repr, Smart};
|
||||||
use crate::visualize::{Color, Gradient, Pattern, RelativeTo};
|
use crate::visualize::{Color, Gradient, RelativeTo, Tiling};
|
||||||
|
|
||||||
/// How a fill or stroke should be painted.
|
/// How a fill or stroke should be painted.
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
@ -12,8 +12,8 @@ pub enum Paint {
|
|||||||
Solid(Color),
|
Solid(Color),
|
||||||
/// A gradient.
|
/// A gradient.
|
||||||
Gradient(Gradient),
|
Gradient(Gradient),
|
||||||
/// A pattern.
|
/// A tiling.
|
||||||
Pattern(Pattern),
|
Tiling(Tiling),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Paint {
|
impl Paint {
|
||||||
@ -21,7 +21,7 @@ impl Paint {
|
|||||||
pub fn unwrap_solid(&self) -> Color {
|
pub fn unwrap_solid(&self) -> Color {
|
||||||
match self {
|
match self {
|
||||||
Self::Solid(color) => *color,
|
Self::Solid(color) => *color,
|
||||||
Self::Gradient(_) | Self::Pattern(_) => panic!("expected solid color"),
|
Self::Gradient(_) | Self::Tiling(_) => panic!("expected solid color"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ impl Paint {
|
|||||||
match self {
|
match self {
|
||||||
Self::Solid(_) => Smart::Auto,
|
Self::Solid(_) => Smart::Auto,
|
||||||
Self::Gradient(gradient) => gradient.relative(),
|
Self::Gradient(gradient) => gradient.relative(),
|
||||||
Self::Pattern(pattern) => pattern.relative(),
|
Self::Tiling(tiling) => tiling.relative(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ impl Paint {
|
|||||||
Self::Gradient(gradient) => {
|
Self::Gradient(gradient) => {
|
||||||
Self::Gradient(gradient.clone().with_relative(RelativeTo::Parent))
|
Self::Gradient(gradient.clone().with_relative(RelativeTo::Parent))
|
||||||
}
|
}
|
||||||
Self::Pattern(pattern) => {
|
Self::Tiling(tiling) => {
|
||||||
Self::Pattern(pattern.clone().with_relative(RelativeTo::Parent))
|
Self::Tiling(tiling.clone().with_relative(RelativeTo::Parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,14 +56,14 @@ impl Debug for Paint {
|
|||||||
match self {
|
match self {
|
||||||
Self::Solid(v) => v.fmt(f),
|
Self::Solid(v) => v.fmt(f),
|
||||||
Self::Gradient(v) => v.fmt(f),
|
Self::Gradient(v) => v.fmt(f),
|
||||||
Self::Pattern(v) => v.fmt(f),
|
Self::Tiling(v) => v.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Pattern> for Paint {
|
impl From<Tiling> for Paint {
|
||||||
fn from(pattern: Pattern) -> Self {
|
fn from(tiling: Tiling) -> Self {
|
||||||
Self::Pattern(pattern)
|
Self::Tiling(tiling)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ impl Repr for Paint {
|
|||||||
match self {
|
match self {
|
||||||
Self::Solid(color) => color.repr(),
|
Self::Solid(color) => color.repr(),
|
||||||
Self::Gradient(gradient) => gradient.repr(),
|
Self::Gradient(gradient) => gradient.repr(),
|
||||||
Self::Pattern(pattern) => pattern.repr(),
|
Self::Tiling(tiling) => tiling.repr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,9 +94,9 @@ cast! {
|
|||||||
self => match self {
|
self => match self {
|
||||||
Self::Solid(color) => color.into_value(),
|
Self::Solid(color) => color.into_value(),
|
||||||
Self::Gradient(gradient) => gradient.into_value(),
|
Self::Gradient(gradient) => gradient.into_value(),
|
||||||
Self::Pattern(pattern) => pattern.into_value(),
|
Self::Tiling(tiling) => tiling.into_value(),
|
||||||
},
|
},
|
||||||
color: Color => Self::Solid(color),
|
color: Color => Self::Solid(color),
|
||||||
gradient: Gradient => Self::Gradient(gradient),
|
gradient: Gradient => Self::Gradient(gradient),
|
||||||
pattern: Pattern => Self::Pattern(pattern),
|
tiling: Tiling => Self::Tiling(tiling),
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use crate::foundations::{
|
|||||||
Resolve, Smart, StyleChain, Value,
|
Resolve, Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::layout::{Abs, Length};
|
use crate::layout::{Abs, Length};
|
||||||
use crate::visualize::{Color, Gradient, Paint, Pattern};
|
use crate::visualize::{Color, Gradient, Paint, Tiling};
|
||||||
|
|
||||||
/// Defines how to draw a line.
|
/// Defines how to draw a line.
|
||||||
///
|
///
|
||||||
@ -213,9 +213,9 @@ impl<T: Numeric> Stroke<T> {
|
|||||||
thickness: self.thickness.map(&f),
|
thickness: self.thickness.map(&f),
|
||||||
cap: self.cap,
|
cap: self.cap,
|
||||||
join: self.join,
|
join: self.join,
|
||||||
dash: self.dash.map(|pattern| {
|
dash: self.dash.map(|dash| {
|
||||||
pattern.map(|pattern| DashPattern {
|
dash.map(|dash| DashPattern {
|
||||||
array: pattern
|
array: dash
|
||||||
.array
|
.array
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|l| match l {
|
.map(|l| match l {
|
||||||
@ -223,7 +223,7 @@ impl<T: Numeric> Stroke<T> {
|
|||||||
DashLength::LineWidth => DashLength::LineWidth,
|
DashLength::LineWidth => DashLength::LineWidth,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
phase: f(pattern.phase),
|
phase: f(dash.phase),
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
miter_limit: self.miter_limit,
|
miter_limit: self.miter_limit,
|
||||||
@ -237,14 +237,10 @@ impl Stroke<Abs> {
|
|||||||
let thickness = self.thickness.unwrap_or(default.thickness);
|
let thickness = self.thickness.unwrap_or(default.thickness);
|
||||||
let dash = self
|
let dash = self
|
||||||
.dash
|
.dash
|
||||||
.map(|pattern| {
|
.map(|dash| {
|
||||||
pattern.map(|pattern| DashPattern {
|
dash.map(|dash| DashPattern {
|
||||||
array: pattern
|
array: dash.array.into_iter().map(|l| l.finish(thickness)).collect(),
|
||||||
.array
|
phase: dash.phase,
|
||||||
.into_iter()
|
|
||||||
.map(|l| l.finish(thickness))
|
|
||||||
.collect(),
|
|
||||||
phase: pattern.phase,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(default.dash);
|
.unwrap_or(default.dash);
|
||||||
@ -372,8 +368,8 @@ cast! {
|
|||||||
paint: Smart::Custom(gradient.into()),
|
paint: Smart::Custom(gradient.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
pattern: Pattern => Self {
|
tiling: Tiling => Self {
|
||||||
paint: Smart::Custom(pattern.into()),
|
paint: Smart::Custom(tiling.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
mut dict: Dict => {
|
mut dict: Dict => {
|
||||||
|
@ -13,18 +13,18 @@ use crate::layout::{Abs, Axes, Frame, Length, Region, Size};
|
|||||||
use crate::visualize::RelativeTo;
|
use crate::visualize::RelativeTo;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// A repeating pattern fill.
|
/// A repeating tiling fill.
|
||||||
///
|
///
|
||||||
/// Typst supports the most common pattern type of tiled patterns, where a
|
/// Typst supports the most common type of tilings, where a pattern is repeated
|
||||||
/// pattern is repeated in a grid-like fashion, covering the entire area of an
|
/// in a grid-like fashion, covering the entire area of an element that is
|
||||||
/// element that is filled or stroked. The pattern is defined by a tile size and
|
/// filled or stroked. The pattern is defined by a tile size and a body defining
|
||||||
/// a body defining the content of each cell. You can also add horizontal or
|
/// the content of each cell. You can also add horizontal or vertical spacing
|
||||||
/// vertical spacing between the cells of the pattern.
|
/// between the cells of the tiling.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #let pat = pattern(size: (30pt, 30pt))[
|
/// #let pat = tiling(size: (30pt, 30pt))[
|
||||||
/// #place(line(start: (0%, 0%), end: (100%, 100%)))
|
/// #place(line(start: (0%, 0%), end: (100%, 100%)))
|
||||||
/// #place(line(start: (0%, 100%), end: (100%, 0%)))
|
/// #place(line(start: (0%, 100%), end: (100%, 0%)))
|
||||||
/// ]
|
/// ]
|
||||||
@ -32,14 +32,14 @@ use crate::World;
|
|||||||
/// #rect(fill: pat, width: 100%, height: 60pt, stroke: 1pt)
|
/// #rect(fill: pat, width: 100%, height: 60pt, stroke: 1pt)
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Patterns are also supported on text, but only when setting the
|
/// Tilings are also supported on text, but only when setting the
|
||||||
/// [relativeness]($pattern.relative) to either `{auto}` (the default value) or
|
/// [relativeness]($tiling.relative) to either `{auto}` (the default value) or
|
||||||
/// `{"parent"}`. To create word-by-word or glyph-by-glyph patterns, you can
|
/// `{"parent"}`. To create word-by-word or glyph-by-glyph tilings, you can
|
||||||
/// wrap the words or characters of your text in [boxes]($box) manually or
|
/// wrap the words or characters of your text in [boxes]($box) manually or
|
||||||
/// through a [show rule]($styling/#show-rules).
|
/// through a [show rule]($styling/#show-rules).
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #let pat = pattern(
|
/// #let pat = tiling(
|
||||||
/// size: (30pt, 30pt),
|
/// size: (30pt, 30pt),
|
||||||
/// relative: "parent",
|
/// relative: "parent",
|
||||||
/// square(
|
/// square(
|
||||||
@ -54,13 +54,13 @@ use crate::World;
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// You can also space the elements further or closer apart using the
|
/// You can also space the elements further or closer apart using the
|
||||||
/// [`spacing`]($pattern.spacing) feature of the pattern. If the spacing
|
/// [`spacing`]($tiling.spacing) feature of the tiling. If the spacing
|
||||||
/// is lower than the size of the pattern, the pattern will overlap.
|
/// is lower than the size of the tiling, the tiling will overlap.
|
||||||
/// If it is higher, the pattern will have gaps of the same color as the
|
/// If it is higher, the tiling will have gaps of the same color as the
|
||||||
/// background of the pattern.
|
/// background of the tiling.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #let pat = pattern(
|
/// #let pat = tiling(
|
||||||
/// size: (30pt, 30pt),
|
/// size: (30pt, 30pt),
|
||||||
/// spacing: (10pt, 10pt),
|
/// spacing: (10pt, 10pt),
|
||||||
/// relative: "parent",
|
/// relative: "parent",
|
||||||
@ -79,11 +79,11 @@ use crate::World;
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Relativeness
|
/// # Relativeness
|
||||||
/// The location of the starting point of the pattern is dependent on the
|
/// The location of the starting point of the tiling is dependent on the
|
||||||
/// dimensions of a container. This container can either be the shape that it is
|
/// dimensions of a container. This container can either be the shape that it is
|
||||||
/// being painted on, or the closest surrounding container. This is controlled
|
/// being painted on, or the closest surrounding container. This is controlled
|
||||||
/// by the `relative` argument of a pattern constructor. By default, patterns
|
/// by the `relative` argument of a tiling constructor. By default, tilings
|
||||||
/// are relative to the shape they are being painted on, unless the pattern is
|
/// are relative to the shape they are being painted on, unless the tiling is
|
||||||
/// applied on text, in which case they are relative to the closest ancestor
|
/// applied on text, in which case they are relative to the closest ancestor
|
||||||
/// container.
|
/// container.
|
||||||
///
|
///
|
||||||
@ -94,29 +94,33 @@ use crate::World;
|
|||||||
/// contains the shape. This includes the boxes and blocks that are implicitly
|
/// contains the shape. This includes the boxes and blocks that are implicitly
|
||||||
/// created by show rules and elements. For example, a [`rotate`] will not
|
/// created by show rules and elements. For example, a [`rotate`] will not
|
||||||
/// affect the parent of a gradient, but a [`grid`] will.
|
/// affect the parent of a gradient, but a [`grid`] will.
|
||||||
#[ty(scope, cast)]
|
///
|
||||||
|
/// # Compatibility
|
||||||
|
/// This type used to be called `pattern`. The name remains as an alias, but is
|
||||||
|
/// deprecated since Typst 0.13.
|
||||||
|
#[ty(scope, cast, keywords = ["pattern"])]
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Pattern(Arc<Repr>);
|
pub struct Tiling(Arc<Repr>);
|
||||||
|
|
||||||
/// Internal representation of [`Pattern`].
|
/// Internal representation of [`Tiling`].
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
struct Repr {
|
struct Repr {
|
||||||
/// The pattern's rendered content.
|
/// The tiling's rendered content.
|
||||||
frame: LazyHash<Frame>,
|
frame: LazyHash<Frame>,
|
||||||
/// The pattern's tile size.
|
/// The tiling's tile size.
|
||||||
size: Size,
|
size: Size,
|
||||||
/// The pattern's tile spacing.
|
/// The tiling's tile spacing.
|
||||||
spacing: Size,
|
spacing: Size,
|
||||||
/// The pattern's relative transform.
|
/// The tiling's relative transform.
|
||||||
relative: Smart<RelativeTo>,
|
relative: Smart<RelativeTo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
impl Pattern {
|
impl Tiling {
|
||||||
/// Construct a new pattern.
|
/// Construct a new tiling.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #let pat = pattern(
|
/// #let pat = tiling(
|
||||||
/// size: (20pt, 20pt),
|
/// size: (20pt, 20pt),
|
||||||
/// relative: "parent",
|
/// relative: "parent",
|
||||||
/// place(
|
/// place(
|
||||||
@ -136,15 +140,15 @@ impl Pattern {
|
|||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// The callsite span.
|
/// The callsite span.
|
||||||
span: Span,
|
span: Span,
|
||||||
/// The bounding box of each cell of the pattern.
|
/// The bounding box of each cell of the tiling.
|
||||||
#[named]
|
#[named]
|
||||||
#[default(Spanned::new(Smart::Auto, Span::detached()))]
|
#[default(Spanned::new(Smart::Auto, Span::detached()))]
|
||||||
size: Spanned<Smart<Axes<Length>>>,
|
size: Spanned<Smart<Axes<Length>>>,
|
||||||
/// The spacing between cells of the pattern.
|
/// The spacing between cells of the tiling.
|
||||||
#[named]
|
#[named]
|
||||||
#[default(Spanned::new(Axes::splat(Length::zero()), Span::detached()))]
|
#[default(Spanned::new(Axes::splat(Length::zero()), Span::detached()))]
|
||||||
spacing: Spanned<Axes<Length>>,
|
spacing: Spanned<Axes<Length>>,
|
||||||
/// The [relative placement](#relativeness) of the pattern.
|
/// The [relative placement](#relativeness) of the tiling.
|
||||||
///
|
///
|
||||||
/// For an element placed at the root/top level of the document, the
|
/// For an element placed at the root/top level of the document, the
|
||||||
/// parent is the page itself. For other elements, the parent is the
|
/// parent is the page itself. For other elements, the parent is the
|
||||||
@ -153,14 +157,14 @@ impl Pattern {
|
|||||||
#[named]
|
#[named]
|
||||||
#[default(Smart::Auto)]
|
#[default(Smart::Auto)]
|
||||||
relative: Smart<RelativeTo>,
|
relative: Smart<RelativeTo>,
|
||||||
/// The content of each cell of the pattern.
|
/// The content of each cell of the tiling.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> SourceResult<Pattern> {
|
) -> SourceResult<Tiling> {
|
||||||
let size_span = size.span;
|
let size_span = size.span;
|
||||||
if let Smart::Custom(size) = size.v {
|
if let Smart::Custom(size) = size.v {
|
||||||
// Ensure that sizes are absolute.
|
// Ensure that sizes are absolute.
|
||||||
if !size.x.em.is_zero() || !size.y.em.is_zero() {
|
if !size.x.em.is_zero() || !size.y.em.is_zero() {
|
||||||
bail!(size_span, "pattern tile size must be absolute");
|
bail!(size_span, "tile size must be absolute");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that sizes are non-zero and finite.
|
// Ensure that sizes are non-zero and finite.
|
||||||
@ -169,25 +173,25 @@ impl Pattern {
|
|||||||
|| !size.x.is_finite()
|
|| !size.x.is_finite()
|
||||||
|| !size.y.is_finite()
|
|| !size.y.is_finite()
|
||||||
{
|
{
|
||||||
bail!(size_span, "pattern tile size must be non-zero and non-infinite");
|
bail!(size_span, "tile size must be non-zero and non-infinite");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that spacing is absolute.
|
// Ensure that spacing is absolute.
|
||||||
if !spacing.v.x.em.is_zero() || !spacing.v.y.em.is_zero() {
|
if !spacing.v.x.em.is_zero() || !spacing.v.y.em.is_zero() {
|
||||||
bail!(spacing.span, "pattern tile spacing must be absolute");
|
bail!(spacing.span, "tile spacing must be absolute");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that spacing is finite.
|
// Ensure that spacing is finite.
|
||||||
if !spacing.v.x.is_finite() || !spacing.v.y.is_finite() {
|
if !spacing.v.x.is_finite() || !spacing.v.y.is_finite() {
|
||||||
bail!(spacing.span, "pattern tile spacing must be finite");
|
bail!(spacing.span, "tile spacing must be finite");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The size of the frame
|
// The size of the frame
|
||||||
let size = size.v.map(|l| l.map(|a| a.abs));
|
let size = size.v.map(|l| l.map(|a| a.abs));
|
||||||
let region = size.unwrap_or_else(|| Axes::splat(Abs::inf()));
|
let region = size.unwrap_or_else(|| Axes::splat(Abs::inf()));
|
||||||
|
|
||||||
// Layout the pattern.
|
// Layout the tiling.
|
||||||
let world = engine.world;
|
let world = engine.world;
|
||||||
let library = world.library();
|
let library = world.library();
|
||||||
let locator = Locator::root();
|
let locator = Locator::root();
|
||||||
@ -204,7 +208,7 @@ impl Pattern {
|
|||||||
// Check that the frame is non-zero.
|
// Check that the frame is non-zero.
|
||||||
if frame.width().is_zero() || frame.height().is_zero() {
|
if frame.width().is_zero() || frame.height().is_zero() {
|
||||||
bail!(
|
bail!(
|
||||||
span, "pattern tile size must be non-zero";
|
span, "tile size must be non-zero";
|
||||||
hint: "try setting the size manually"
|
hint: "try setting the size manually"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -218,8 +222,8 @@ impl Pattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pattern {
|
impl Tiling {
|
||||||
/// Set the relative placement of the pattern.
|
/// Set the relative placement of the tiling.
|
||||||
pub fn with_relative(mut self, relative: RelativeTo) -> Self {
|
pub fn with_relative(mut self, relative: RelativeTo) -> Self {
|
||||||
if let Some(this) = Arc::get_mut(&mut self.0) {
|
if let Some(this) = Arc::get_mut(&mut self.0) {
|
||||||
this.relative = Smart::Custom(relative);
|
this.relative = Smart::Custom(relative);
|
||||||
@ -233,27 +237,27 @@ impl Pattern {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the frame of the pattern.
|
/// Return the frame of the tiling.
|
||||||
pub fn frame(&self) -> &Frame {
|
pub fn frame(&self) -> &Frame {
|
||||||
&self.0.frame
|
&self.0.frame
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the size of the pattern in absolute units.
|
/// Return the size of the tiling in absolute units.
|
||||||
pub fn size(&self) -> Size {
|
pub fn size(&self) -> Size {
|
||||||
self.0.size
|
self.0.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the spacing of the pattern in absolute units.
|
/// Return the spacing of the tiling in absolute units.
|
||||||
pub fn spacing(&self) -> Size {
|
pub fn spacing(&self) -> Size {
|
||||||
self.0.spacing
|
self.0.spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the relative placement of the pattern.
|
/// Returns the relative placement of the tiling.
|
||||||
pub fn relative(&self) -> Smart<RelativeTo> {
|
pub fn relative(&self) -> Smart<RelativeTo> {
|
||||||
self.0.relative
|
self.0.relative
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the relative placement of the pattern.
|
/// Returns the relative placement of the tiling.
|
||||||
pub fn unwrap_relative(&self, on_text: bool) -> RelativeTo {
|
pub fn unwrap_relative(&self, on_text: bool) -> RelativeTo {
|
||||||
self.0.relative.unwrap_or_else(|| {
|
self.0.relative.unwrap_or_else(|| {
|
||||||
if on_text {
|
if on_text {
|
||||||
@ -265,10 +269,10 @@ impl Pattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl repr::Repr for Pattern {
|
impl repr::Repr for Tiling {
|
||||||
fn repr(&self) -> EcoString {
|
fn repr(&self) -> EcoString {
|
||||||
let mut out =
|
let mut out =
|
||||||
eco_format!("pattern(({}, {})", self.0.size.x.repr(), self.0.size.y.repr());
|
eco_format!("tiling(({}, {})", self.0.size.x.repr(), self.0.size.y.repr());
|
||||||
|
|
||||||
if self.0.spacing.is_zero() {
|
if self.0.spacing.is_zero() {
|
||||||
out.push_str(", spacing: (");
|
out.push_str(", spacing: (");
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use tiny_skia as sk;
|
use tiny_skia as sk;
|
||||||
use typst_library::layout::{Axes, Point, Ratio, Size};
|
use typst_library::layout::{Axes, Point, Ratio, Size};
|
||||||
use typst_library::visualize::{Color, Gradient, Paint, Pattern, RelativeTo};
|
use typst_library::visualize::{Color, Gradient, Paint, RelativeTo, Tiling};
|
||||||
|
|
||||||
use crate::{AbsExt, State};
|
use crate::{AbsExt, State};
|
||||||
|
|
||||||
@ -72,26 +72,26 @@ impl PaintSampler for GradientSampler<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State used when sampling patterns for text.
|
/// State used when sampling tilings for text.
|
||||||
///
|
///
|
||||||
/// It caches the inverse transform to the parent, so that we can
|
/// It caches the inverse transform to the parent, so that we can
|
||||||
/// reuse it instead of recomputing it for each pixel.
|
/// reuse it instead of recomputing it for each pixel.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct PatternSampler<'a> {
|
pub struct TilingSampler<'a> {
|
||||||
size: Size,
|
size: Size,
|
||||||
transform_to_parent: sk::Transform,
|
transform_to_parent: sk::Transform,
|
||||||
pixmap: &'a sk::Pixmap,
|
pixmap: &'a sk::Pixmap,
|
||||||
pixel_per_pt: f32,
|
pixel_per_pt: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PatternSampler<'a> {
|
impl<'a> TilingSampler<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pattern: &'a Pattern,
|
tilings: &'a Tiling,
|
||||||
pixmap: &'a sk::Pixmap,
|
pixmap: &'a sk::Pixmap,
|
||||||
state: &State,
|
state: &State,
|
||||||
on_text: bool,
|
on_text: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let relative = pattern.unwrap_relative(on_text);
|
let relative = tilings.unwrap_relative(on_text);
|
||||||
let fill_transform = match relative {
|
let fill_transform = match relative {
|
||||||
RelativeTo::Self_ => sk::Transform::identity(),
|
RelativeTo::Self_ => sk::Transform::identity(),
|
||||||
RelativeTo::Parent => state.container_transform.invert().unwrap(),
|
RelativeTo::Parent => state.container_transform.invert().unwrap(),
|
||||||
@ -99,17 +99,17 @@ impl<'a> PatternSampler<'a> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
pixmap,
|
pixmap,
|
||||||
size: (pattern.size() + pattern.spacing()) * state.pixel_per_pt as f64,
|
size: (tilings.size() + tilings.spacing()) * state.pixel_per_pt as f64,
|
||||||
transform_to_parent: fill_transform,
|
transform_to_parent: fill_transform,
|
||||||
pixel_per_pt: state.pixel_per_pt,
|
pixel_per_pt: state.pixel_per_pt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintSampler for PatternSampler<'_> {
|
impl PaintSampler for TilingSampler<'_> {
|
||||||
/// Samples a single point in a glyph.
|
/// Samples a single point in a glyph.
|
||||||
fn sample(self, (x, y): (u32, u32)) -> sk::PremultipliedColorU8 {
|
fn sample(self, (x, y): (u32, u32)) -> sk::PremultipliedColorU8 {
|
||||||
// Compute the point in the pattern's coordinate space.
|
// Compute the point in the tilings's coordinate space.
|
||||||
let mut point = sk::Point { x: x as f32, y: y as f32 };
|
let mut point = sk::Point { x: x as f32, y: y as f32 };
|
||||||
self.transform_to_parent.map_point(&mut point);
|
self.transform_to_parent.map_point(&mut point);
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ impl PaintSampler for PatternSampler<'_> {
|
|||||||
let y =
|
let y =
|
||||||
(point.y * self.pixel_per_pt).rem_euclid(self.size.y.to_f32()).floor() as u32;
|
(point.y * self.pixel_per_pt).rem_euclid(self.size.y.to_f32()).floor() as u32;
|
||||||
|
|
||||||
// Sample the pattern
|
// Sample the tilings
|
||||||
self.pixmap.pixel(x, y).unwrap()
|
self.pixmap.pixel(x, y).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,8 +218,8 @@ pub fn to_sk_paint<'a>(
|
|||||||
|
|
||||||
sk_paint.anti_alias = gradient.anti_alias();
|
sk_paint.anti_alias = gradient.anti_alias();
|
||||||
}
|
}
|
||||||
Paint::Pattern(pattern) => {
|
Paint::Tiling(tilings) => {
|
||||||
let relative = pattern.unwrap_relative(on_text);
|
let relative = tilings.unwrap_relative(on_text);
|
||||||
|
|
||||||
let fill_transform = match relative {
|
let fill_transform = match relative {
|
||||||
RelativeTo::Self_ => fill_transform.unwrap_or_default(),
|
RelativeTo::Self_ => fill_transform.unwrap_or_default(),
|
||||||
@ -228,7 +228,7 @@ pub fn to_sk_paint<'a>(
|
|||||||
.post_concat(state.transform.invert().unwrap()),
|
.post_concat(state.transform.invert().unwrap()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let canvas = render_pattern_frame(&state, pattern);
|
let canvas = render_tiling_frame(&state, tilings);
|
||||||
*pixmap = Some(Arc::new(canvas));
|
*pixmap = Some(Arc::new(canvas));
|
||||||
|
|
||||||
let offset = match relative {
|
let offset = match relative {
|
||||||
@ -265,17 +265,17 @@ pub fn to_sk_color_u8(color: Color) -> sk::ColorU8 {
|
|||||||
sk::ColorU8::from_rgba(r, g, b, a)
|
sk::ColorU8::from_rgba(r, g, b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_pattern_frame(state: &State, pattern: &Pattern) -> sk::Pixmap {
|
pub fn render_tiling_frame(state: &State, tilings: &Tiling) -> sk::Pixmap {
|
||||||
let size = pattern.size() + pattern.spacing();
|
let size = tilings.size() + tilings.spacing();
|
||||||
let mut canvas = sk::Pixmap::new(
|
let mut canvas = sk::Pixmap::new(
|
||||||
(size.x.to_f32() * state.pixel_per_pt).round() as u32,
|
(size.x.to_f32() * state.pixel_per_pt).round() as u32,
|
||||||
(size.y.to_f32() * state.pixel_per_pt).round() as u32,
|
(size.y.to_f32() * state.pixel_per_pt).round() as u32,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Render the pattern into a new canvas.
|
// Render the tilings into a new canvas.
|
||||||
let ts = sk::Transform::from_scale(state.pixel_per_pt, state.pixel_per_pt);
|
let ts = sk::Transform::from_scale(state.pixel_per_pt, state.pixel_per_pt);
|
||||||
let temp_state = State::new(pattern.size(), ts, state.pixel_per_pt);
|
let temp_state = State::new(tilings.size(), ts, state.pixel_per_pt);
|
||||||
crate::render_frame(&mut canvas, temp_state, pattern.frame());
|
crate::render_frame(&mut canvas, temp_state, tilings.frame());
|
||||||
canvas
|
canvas
|
||||||
}
|
}
|
||||||
|
@ -168,11 +168,11 @@ pub fn to_sk_line_join(join: LineJoin) -> sk::LineJoin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_sk_dash_pattern(pattern: &DashPattern<Abs, Abs>) -> Option<sk::StrokeDash> {
|
pub fn to_sk_dash_pattern(dash: &DashPattern<Abs, Abs>) -> Option<sk::StrokeDash> {
|
||||||
// tiny-skia only allows dash patterns with an even number of elements,
|
// tiny-skia only allows dash patterns with an even number of elements,
|
||||||
// while pdf allows any number.
|
// while pdf allows any number.
|
||||||
let pattern_len = pattern.array.len();
|
let pattern_len = dash.array.len();
|
||||||
let len = if pattern_len % 2 == 1 { 2 * pattern_len } else { pattern_len };
|
let len = if pattern_len % 2 == 1 { 2 * pattern_len } else { pattern_len };
|
||||||
let dash_array = pattern.array.iter().map(|l| l.to_f32()).cycle().take(len).collect();
|
let dash_array = dash.array.iter().map(|l| l.to_f32()).cycle().take(len).collect();
|
||||||
sk::StrokeDash::new(dash_array, pattern.phase.to_f32())
|
sk::StrokeDash::new(dash_array, dash.phase.to_f32())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use typst_library::text::color::{glyph_frame, should_outline};
|
|||||||
use typst_library::text::{Font, TextItem};
|
use typst_library::text::{Font, TextItem};
|
||||||
use typst_library::visualize::{FixedStroke, Paint};
|
use typst_library::visualize::{FixedStroke, Paint};
|
||||||
|
|
||||||
use crate::paint::{self, GradientSampler, PaintSampler, PatternSampler};
|
use crate::paint::{self, GradientSampler, PaintSampler, TilingSampler};
|
||||||
use crate::{shape, AbsExt, State};
|
use crate::{shape, AbsExt, State};
|
||||||
|
|
||||||
/// Render a text run into the canvas.
|
/// Render a text run into the canvas.
|
||||||
@ -145,9 +145,9 @@ fn render_outline_glyph(
|
|||||||
paint::to_sk_color_u8(*color).premultiply(),
|
paint::to_sk_color_u8(*color).premultiply(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Paint::Pattern(pattern) => {
|
Paint::Tiling(tiling) => {
|
||||||
let pixmap = paint::render_pattern_frame(&state, pattern);
|
let pixmap = paint::render_tiling_frame(&state, tiling);
|
||||||
let sampler = PatternSampler::new(pattern, &pixmap, &state, true);
|
let sampler = TilingSampler::new(tiling, &pixmap, &state, true);
|
||||||
write_bitmap(canvas, &bitmap, &state, sampler)?;
|
write_bitmap(canvas, &bitmap, &state, sampler)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ use typst_library::layout::{
|
|||||||
Abs, Frame, FrameItem, FrameKind, GroupItem, Page, PagedDocument, Point, Ratio, Size,
|
Abs, Frame, FrameItem, FrameKind, GroupItem, Page, PagedDocument, Point, Ratio, Size,
|
||||||
Transform,
|
Transform,
|
||||||
};
|
};
|
||||||
use typst_library::visualize::{Geometry, Gradient, Pattern};
|
use typst_library::visualize::{Geometry, Gradient, Tiling};
|
||||||
use typst_utils::hash128;
|
use typst_utils::hash128;
|
||||||
use xmlwriter::XmlWriter;
|
use xmlwriter::XmlWriter;
|
||||||
|
|
||||||
use crate::paint::{GradientRef, PatternRef, SVGSubGradient};
|
use crate::paint::{GradientRef, SVGSubGradient, TilingRef};
|
||||||
use crate::text::RenderedGlyph;
|
use crate::text::RenderedGlyph;
|
||||||
|
|
||||||
/// Export a frame into a SVG file.
|
/// Export a frame into a SVG file.
|
||||||
@ -92,12 +92,12 @@ struct SVGRenderer {
|
|||||||
/// different transforms. Therefore this allows us to reuse the same gradient
|
/// different transforms. Therefore this allows us to reuse the same gradient
|
||||||
/// multiple times.
|
/// multiple times.
|
||||||
gradient_refs: Deduplicator<GradientRef>,
|
gradient_refs: Deduplicator<GradientRef>,
|
||||||
/// Deduplicated patterns with transform matrices. They use a reference
|
/// Deduplicated tilings with transform matrices. They use a reference
|
||||||
/// (`href`) to a "source" pattern instead of being defined inline.
|
/// (`href`) to a "source" tiling instead of being defined inline.
|
||||||
/// This saves a lot of space since patterns are often reused but with
|
/// This saves a lot of space since tilings are often reused but with
|
||||||
/// different transforms. Therefore this allows us to reuse the same gradient
|
/// different transforms. Therefore this allows us to reuse the same gradient
|
||||||
/// multiple times.
|
/// multiple times.
|
||||||
pattern_refs: Deduplicator<PatternRef>,
|
tiling_refs: Deduplicator<TilingRef>,
|
||||||
/// These are the actual gradients being written in the SVG file.
|
/// These are the actual gradients being written in the SVG file.
|
||||||
/// These gradients are deduplicated because they do not contain the transform
|
/// These gradients are deduplicated because they do not contain the transform
|
||||||
/// matrix, allowing them to be reused across multiple invocations.
|
/// matrix, allowing them to be reused across multiple invocations.
|
||||||
@ -105,12 +105,12 @@ struct SVGRenderer {
|
|||||||
/// The `Ratio` is the aspect ratio of the gradient, this is used to correct
|
/// The `Ratio` is the aspect ratio of the gradient, this is used to correct
|
||||||
/// the angle of the gradient.
|
/// the angle of the gradient.
|
||||||
gradients: Deduplicator<(Gradient, Ratio)>,
|
gradients: Deduplicator<(Gradient, Ratio)>,
|
||||||
/// These are the actual patterns being written in the SVG file.
|
/// These are the actual tilings being written in the SVG file.
|
||||||
/// These patterns are deduplicated because they do not contain the transform
|
/// These tilings are deduplicated because they do not contain the transform
|
||||||
/// matrix, allowing them to be reused across multiple invocations.
|
/// matrix, allowing them to be reused across multiple invocations.
|
||||||
///
|
///
|
||||||
/// The `String` is the rendered pattern frame.
|
/// The `String` is the rendered tiling frame.
|
||||||
patterns: Deduplicator<Pattern>,
|
tilings: Deduplicator<Tiling>,
|
||||||
/// These are the gradients that compose a conic gradient.
|
/// These are the gradients that compose a conic gradient.
|
||||||
conic_subgradients: Deduplicator<SVGSubGradient>,
|
conic_subgradients: Deduplicator<SVGSubGradient>,
|
||||||
}
|
}
|
||||||
@ -163,8 +163,8 @@ impl SVGRenderer {
|
|||||||
gradient_refs: Deduplicator::new('g'),
|
gradient_refs: Deduplicator::new('g'),
|
||||||
gradients: Deduplicator::new('f'),
|
gradients: Deduplicator::new('f'),
|
||||||
conic_subgradients: Deduplicator::new('s'),
|
conic_subgradients: Deduplicator::new('s'),
|
||||||
pattern_refs: Deduplicator::new('p'),
|
tiling_refs: Deduplicator::new('p'),
|
||||||
patterns: Deduplicator::new('t'),
|
tilings: Deduplicator::new('t'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,8 +272,8 @@ impl SVGRenderer {
|
|||||||
self.write_gradients();
|
self.write_gradients();
|
||||||
self.write_gradient_refs();
|
self.write_gradient_refs();
|
||||||
self.write_subgradients();
|
self.write_subgradients();
|
||||||
self.write_patterns();
|
self.write_tilings();
|
||||||
self.write_pattern_refs();
|
self.write_tiling_refs();
|
||||||
self.xml.end_document()
|
self.xml.end_document()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use ecow::{eco_format, EcoString};
|
|||||||
use ttf_parser::OutlineBuilder;
|
use ttf_parser::OutlineBuilder;
|
||||||
use typst_library::foundations::Repr;
|
use typst_library::foundations::Repr;
|
||||||
use typst_library::layout::{Angle, Axes, Frame, Quadrant, Ratio, Size, Transform};
|
use typst_library::layout::{Angle, Axes, Frame, Quadrant, Ratio, Size, Transform};
|
||||||
use typst_library::visualize::{Color, FillRule, Gradient, Paint, Pattern, RatioOrAngle};
|
use typst_library::visualize::{Color, FillRule, Gradient, Paint, RatioOrAngle, Tiling};
|
||||||
use typst_utils::hash128;
|
use typst_utils::hash128;
|
||||||
use xmlwriter::XmlWriter;
|
use xmlwriter::XmlWriter;
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const CONIC_SEGMENT: usize = 360;
|
|||||||
|
|
||||||
impl SVGRenderer {
|
impl SVGRenderer {
|
||||||
/// Render a frame to a string.
|
/// Render a frame to a string.
|
||||||
pub(super) fn render_pattern_frame(
|
pub(super) fn render_tiling_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: State,
|
state: State,
|
||||||
ts: Transform,
|
ts: Transform,
|
||||||
@ -44,8 +44,8 @@ impl SVGRenderer {
|
|||||||
let id = self.push_gradient(gradient, size, ts);
|
let id = self.push_gradient(gradient, size, ts);
|
||||||
self.xml.write_attribute_fmt("fill", format_args!("url(#{id})"));
|
self.xml.write_attribute_fmt("fill", format_args!("url(#{id})"));
|
||||||
}
|
}
|
||||||
Paint::Pattern(pattern) => {
|
Paint::Tiling(tiling) => {
|
||||||
let id = self.push_pattern(pattern, size, ts);
|
let id = self.push_tiling(tiling, size, ts);
|
||||||
self.xml.write_attribute_fmt("fill", format_args!("url(#{id})"));
|
self.xml.write_attribute_fmt("fill", format_args!("url(#{id})"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,30 +86,29 @@ impl SVGRenderer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn push_pattern(
|
pub(super) fn push_tiling(
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &Pattern,
|
tiling: &Tiling,
|
||||||
size: Size,
|
size: Size,
|
||||||
ts: Transform,
|
ts: Transform,
|
||||||
) -> Id {
|
) -> Id {
|
||||||
let pattern_size = pattern.size() + pattern.spacing();
|
let tiling_size = tiling.size() + tiling.spacing();
|
||||||
// Unfortunately due to a limitation of `xmlwriter`, we need to
|
// Unfortunately due to a limitation of `xmlwriter`, we need to
|
||||||
// render the frame twice: once to allocate all of the resources
|
// render the frame twice: once to allocate all of the resources
|
||||||
// that it needs and once to actually render it.
|
// that it needs and once to actually render it.
|
||||||
self.render_pattern_frame(
|
self.render_tiling_frame(
|
||||||
State::new(pattern_size, Transform::identity()),
|
State::new(tiling_size, Transform::identity()),
|
||||||
Transform::identity(),
|
Transform::identity(),
|
||||||
pattern.frame(),
|
tiling.frame(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let pattern_id = self.patterns.insert_with(hash128(pattern), || pattern.clone());
|
let tiling_id = self.tilings.insert_with(hash128(tiling), || tiling.clone());
|
||||||
self.pattern_refs
|
self.tiling_refs.insert_with(hash128(&(tiling_id, ts)), || TilingRef {
|
||||||
.insert_with(hash128(&(pattern_id, ts)), || PatternRef {
|
id: tiling_id,
|
||||||
id: pattern_id,
|
|
||||||
transform: ts,
|
transform: ts,
|
||||||
ratio: Axes::new(
|
ratio: Axes::new(
|
||||||
Ratio::new(pattern_size.x.to_pt() / size.x.to_pt()),
|
Ratio::new(tiling_size.x.to_pt() / size.x.to_pt()),
|
||||||
Ratio::new(pattern_size.y.to_pt() / size.y.to_pt()),
|
Ratio::new(tiling_size.y.to_pt() / size.y.to_pt()),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -188,12 +187,12 @@ impl SVGRenderer {
|
|||||||
// Create the path for the segment.
|
// Create the path for the segment.
|
||||||
let mut builder = SvgPathBuilder::default();
|
let mut builder = SvgPathBuilder::default();
|
||||||
builder.move_to(
|
builder.move_to(
|
||||||
correct_pattern_pos(center.0),
|
correct_tiling_pos(center.0),
|
||||||
correct_pattern_pos(center.1),
|
correct_tiling_pos(center.1),
|
||||||
);
|
);
|
||||||
builder.line_to(
|
builder.line_to(
|
||||||
correct_pattern_pos(-2.0 * (theta1 + angle).cos() + center.0),
|
correct_tiling_pos(-2.0 * (theta1 + angle).cos() + center.0),
|
||||||
correct_pattern_pos(2.0 * (theta1 + angle).sin() + center.1),
|
correct_tiling_pos(2.0 * (theta1 + angle).sin() + center.1),
|
||||||
);
|
);
|
||||||
builder.arc(
|
builder.arc(
|
||||||
(2.0, 2.0),
|
(2.0, 2.0),
|
||||||
@ -201,10 +200,10 @@ impl SVGRenderer {
|
|||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
(
|
(
|
||||||
correct_pattern_pos(
|
correct_tiling_pos(
|
||||||
-2.0 * (theta2 + angle).cos() + center.0,
|
-2.0 * (theta2 + angle).cos() + center.0,
|
||||||
),
|
),
|
||||||
correct_pattern_pos(
|
correct_tiling_pos(
|
||||||
2.0 * (theta2 + angle).sin() + center.1,
|
2.0 * (theta2 + angle).sin() + center.1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -370,19 +369,19 @@ impl SVGRenderer {
|
|||||||
self.xml.end_element();
|
self.xml.end_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the raw gradients (without transform) to the SVG file.
|
/// Write the raw tilings (without transform) to the SVG file.
|
||||||
pub(super) fn write_patterns(&mut self) {
|
pub(super) fn write_tilings(&mut self) {
|
||||||
if self.patterns.is_empty() {
|
if self.tilings.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.xml.start_element("defs");
|
self.xml.start_element("defs");
|
||||||
self.xml.write_attribute("id", "patterns");
|
self.xml.write_attribute("id", "tilings");
|
||||||
|
|
||||||
for (id, pattern) in
|
for (id, tiling) in
|
||||||
self.patterns.iter().map(|(i, p)| (i, p.clone())).collect::<Vec<_>>()
|
self.tilings.iter().map(|(i, p)| (i, p.clone())).collect::<Vec<_>>()
|
||||||
{
|
{
|
||||||
let size = pattern.size() + pattern.spacing();
|
let size = tiling.size() + tiling.spacing();
|
||||||
self.xml.start_element("pattern");
|
self.xml.start_element("pattern");
|
||||||
self.xml.write_attribute("id", &id);
|
self.xml.write_attribute("id", &id);
|
||||||
self.xml.write_attribute("width", &size.x.to_pt());
|
self.xml.write_attribute("width", &size.x.to_pt());
|
||||||
@ -396,7 +395,7 @@ impl SVGRenderer {
|
|||||||
// Render the frame.
|
// Render the frame.
|
||||||
let state = State::new(size, Transform::identity());
|
let state = State::new(size, Transform::identity());
|
||||||
let ts = Transform::identity();
|
let ts = Transform::identity();
|
||||||
self.render_frame(state, ts, pattern.frame());
|
self.render_frame(state, ts, tiling.frame());
|
||||||
|
|
||||||
self.xml.end_element();
|
self.xml.end_element();
|
||||||
}
|
}
|
||||||
@ -404,28 +403,28 @@ impl SVGRenderer {
|
|||||||
self.xml.end_element()
|
self.xml.end_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the references to the deduplicated patterns for each usage site.
|
/// Writes the references to the deduplicated tilings for each usage site.
|
||||||
pub(super) fn write_pattern_refs(&mut self) {
|
pub(super) fn write_tiling_refs(&mut self) {
|
||||||
if self.pattern_refs.is_empty() {
|
if self.tiling_refs.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.xml.start_element("defs");
|
self.xml.start_element("defs");
|
||||||
self.xml.write_attribute("id", "pattern-refs");
|
self.xml.write_attribute("id", "tilings-refs");
|
||||||
for (id, pattern_ref) in self.pattern_refs.iter() {
|
for (id, tiling_ref) in self.tiling_refs.iter() {
|
||||||
self.xml.start_element("pattern");
|
self.xml.start_element("pattern");
|
||||||
self.xml
|
self.xml
|
||||||
.write_attribute("patternTransform", &SvgMatrix(pattern_ref.transform));
|
.write_attribute("patternTransform", &SvgMatrix(tiling_ref.transform));
|
||||||
|
|
||||||
self.xml.write_attribute("id", &id);
|
self.xml.write_attribute("id", &id);
|
||||||
|
|
||||||
// Writing the href attribute to the "reference" pattern.
|
// Writing the href attribute to the "reference" pattern.
|
||||||
self.xml
|
self.xml
|
||||||
.write_attribute_fmt("href", format_args!("#{}", pattern_ref.id));
|
.write_attribute_fmt("href", format_args!("#{}", tiling_ref.id));
|
||||||
|
|
||||||
// Also writing the xlink:href attribute for compatibility.
|
// Also writing the xlink:href attribute for compatibility.
|
||||||
self.xml
|
self.xml
|
||||||
.write_attribute_fmt("xlink:href", format_args!("#{}", pattern_ref.id));
|
.write_attribute_fmt("xlink:href", format_args!("#{}", tiling_ref.id));
|
||||||
self.xml.end_element();
|
self.xml.end_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,15 +432,15 @@ impl SVGRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to a deduplicated pattern, with a transform matrix.
|
/// A reference to a deduplicated tiling, with a transform matrix.
|
||||||
///
|
///
|
||||||
/// Allows patterns to be reused across multiple invocations,
|
/// Allows tilings to be reused across multiple invocations, simply by changing
|
||||||
/// simply by changing the transform matrix.
|
/// the transform matrix.
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
pub struct PatternRef {
|
pub struct TilingRef {
|
||||||
/// The ID of the deduplicated gradient
|
/// The ID of the deduplicated gradient
|
||||||
id: Id,
|
id: Id,
|
||||||
/// The transform matrix to apply to the pattern.
|
/// The transform matrix to apply to the tiling.
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
/// The ratio of the size of the cell to the size of the filled area.
|
/// The ratio of the size of the cell to the size of the filled area.
|
||||||
ratio: Axes<Ratio>,
|
ratio: Axes<Ratio>,
|
||||||
@ -587,7 +586,7 @@ impl ColorEncode for Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a coordinate in a unit size square to a coordinate in the pattern.
|
/// Maps a coordinate in a unit size square to a coordinate in the tiling.
|
||||||
pub fn correct_pattern_pos(x: f32) -> f32 {
|
pub fn correct_tiling_pos(x: f32) -> f32 {
|
||||||
(x + 0.5) / 2.0
|
(x + 0.5) / 2.0
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,8 @@ impl SVGRenderer {
|
|||||||
)
|
)
|
||||||
.post_concat(state.transform.invert().unwrap()),
|
.post_concat(state.transform.invert().unwrap()),
|
||||||
}
|
}
|
||||||
} else if let Paint::Pattern(pattern) = paint {
|
} else if let Paint::Tiling(tiling) = paint {
|
||||||
match pattern.unwrap_relative(false) {
|
match tiling.unwrap_relative(false) {
|
||||||
RelativeTo::Self_ => Transform::identity(),
|
RelativeTo::Self_ => Transform::identity(),
|
||||||
RelativeTo::Parent => state.transform.invert().unwrap(),
|
RelativeTo::Parent => state.transform.invert().unwrap(),
|
||||||
}
|
}
|
||||||
@ -112,8 +112,8 @@ impl SVGRenderer {
|
|||||||
let id = self.push_gradient(gradient, size, fill_transform);
|
let id = self.push_gradient(gradient, size, fill_transform);
|
||||||
self.xml.write_attribute_fmt("stroke", format_args!("url(#{id})"));
|
self.xml.write_attribute_fmt("stroke", format_args!("url(#{id})"));
|
||||||
}
|
}
|
||||||
Paint::Pattern(pattern) => {
|
Paint::Tiling(tiling) => {
|
||||||
let id = self.push_pattern(pattern, size, fill_transform);
|
let id = self.push_tiling(tiling, size, fill_transform);
|
||||||
self.xml.write_attribute_fmt("stroke", format_args!("url(#{id})"));
|
self.xml.write_attribute_fmt("stroke", format_args!("url(#{id})"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,11 +137,11 @@ impl SVGRenderer {
|
|||||||
);
|
);
|
||||||
self.xml
|
self.xml
|
||||||
.write_attribute("stroke-miterlimit", &stroke.miter_limit.get());
|
.write_attribute("stroke-miterlimit", &stroke.miter_limit.get());
|
||||||
if let Some(pattern) = &stroke.dash {
|
if let Some(dash) = &stroke.dash {
|
||||||
self.xml.write_attribute("stroke-dashoffset", &pattern.phase.to_pt());
|
self.xml.write_attribute("stroke-dashoffset", &dash.phase.to_pt());
|
||||||
self.xml.write_attribute(
|
self.xml.write_attribute(
|
||||||
"stroke-dasharray",
|
"stroke-dasharray",
|
||||||
&pattern
|
&dash
|
||||||
.array
|
.array
|
||||||
.iter()
|
.iter()
|
||||||
.map(|dash| dash.to_pt().to_string())
|
.map(|dash| dash.to_pt().to_string())
|
||||||
|
@ -165,7 +165,7 @@ impl SVGRenderer {
|
|||||||
)
|
)
|
||||||
.post_concat(state.transform.invert().unwrap()),
|
.post_concat(state.transform.invert().unwrap()),
|
||||||
},
|
},
|
||||||
Paint::Pattern(pattern) => match pattern.unwrap_relative(true) {
|
Paint::Tiling(tiling) => match tiling.unwrap_relative(true) {
|
||||||
RelativeTo::Self_ => Transform::identity(),
|
RelativeTo::Self_ => Transform::identity(),
|
||||||
RelativeTo::Parent => state.transform.invert().unwrap(),
|
RelativeTo::Parent => state.transform.invert().unwrap(),
|
||||||
},
|
},
|
||||||
|
@ -163,7 +163,7 @@ pub enum Expr<'a> {
|
|||||||
Parenthesized(Parenthesized<'a>),
|
Parenthesized(Parenthesized<'a>),
|
||||||
/// An array: `(1, "hi", 12cm)`.
|
/// An array: `(1, "hi", 12cm)`.
|
||||||
Array(Array<'a>),
|
Array(Array<'a>),
|
||||||
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary: `(thickness: 3pt, dash: "solid")`.
|
||||||
Dict(Dict<'a>),
|
Dict(Dict<'a>),
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
Unary(Unary<'a>),
|
Unary(Unary<'a>),
|
||||||
@ -1195,7 +1195,7 @@ impl<'a> AstNode<'a> for ArrayItem<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary: `(thickness: 3pt, dash: "solid")`.
|
||||||
Dict
|
Dict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ pub enum SyntaxKind {
|
|||||||
Parenthesized,
|
Parenthesized,
|
||||||
/// An array: `(1, "hi", 12cm)`.
|
/// An array: `(1, "hi", 12cm)`.
|
||||||
Array,
|
Array,
|
||||||
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary: `(thickness: 3pt, dash: "solid")`.
|
||||||
Dict,
|
Dict,
|
||||||
/// A named pair: `thickness: 3pt`.
|
/// A named pair: `thickness: 3pt`.
|
||||||
Named,
|
Named,
|
||||||
|
@ -1077,7 +1077,7 @@ fn expr_with_paren(p: &mut Parser, atomic: bool) {
|
|||||||
/// Parses either
|
/// Parses either
|
||||||
/// - a parenthesized expression: `(1 + 2)`, or
|
/// - a parenthesized expression: `(1 + 2)`, or
|
||||||
/// - an array: `(1, "hi", 12cm)`, or
|
/// - an array: `(1, "hi", 12cm)`, or
|
||||||
/// - a dictionary: `(thickness: 3pt, pattern: dashed)`.
|
/// - a dictionary: `(thickness: 3pt, dash: "solid")`.
|
||||||
fn parenthesized_or_array_or_dict(p: &mut Parser) -> SyntaxKind {
|
fn parenthesized_or_array_or_dict(p: &mut Parser) -> SyntaxKind {
|
||||||
let mut state = GroupState {
|
let mut state = GroupState {
|
||||||
count: 0,
|
count: 0,
|
||||||
|
@ -31,7 +31,7 @@ description: Changes in Typst 0.10.0
|
|||||||
- More LaTeX commands (e.g. for quotes) are now respected in `.bib` files
|
- More LaTeX commands (e.g. for quotes) are now respected in `.bib` files
|
||||||
|
|
||||||
## Visualization
|
## Visualization
|
||||||
- Added support for [patterns]($pattern) as fills and strokes
|
- Added support for [patterns]($tiling) as fills and strokes
|
||||||
- The `alpha` parameter of the [`components`]($color.components) function on
|
- The `alpha` parameter of the [`components`]($color.components) function on
|
||||||
colors is now a named parameter **(Breaking change)**
|
colors is now a named parameter **(Breaking change)**
|
||||||
- Added support for the [Oklch]($color.oklch) color space
|
- Added support for the [Oklch]($color.oklch) color space
|
||||||
|
@ -59,13 +59,13 @@ _Thanks to [@PgBiel](https://github.com/PgBiel) for his work on tables!_
|
|||||||
- When context is available, [`counter.display`] now directly returns the result
|
- When context is available, [`counter.display`] now directly returns the result
|
||||||
of applying the numbering instead of yielding opaque content. It should not be
|
of applying the numbering instead of yielding opaque content. It should not be
|
||||||
used anymore without context. (Deprecation planned)
|
used anymore without context. (Deprecation planned)
|
||||||
- The [`state.display`] function should not be used anymore, use [`state.get`]
|
- The `state.display` function should not be used anymore, use [`state.get`]
|
||||||
instead (Deprecation planned)
|
instead (Deprecation planned)
|
||||||
- The `location` argument of [`query`], [`counter.final`], and [`state.final`]
|
- The `location` argument of [`query`], [`counter.final`], and [`state.final`]
|
||||||
should not be used anymore (Deprecation planned)
|
should not be used anymore (Deprecation planned)
|
||||||
- The [`styles`]($measure.styles) argument of the `measure` function should not
|
- The `styles` argument of the `measure` function should not be used anymore
|
||||||
be used anymore (Deprecation planned)
|
(Deprecation planned)
|
||||||
- The [`style`] function should not be used anymore, use context instead
|
- The `style` function should not be used anymore, use context instead
|
||||||
(Deprecation planned)
|
(Deprecation planned)
|
||||||
- The correct context is now also provided in various other places where it is
|
- The correct context is now also provided in various other places where it is
|
||||||
available, e.g. in show rules, layout callbacks, and numbering functions in
|
available, e.g. in show rules, layout callbacks, and numbering functions in
|
||||||
|
@ -37,7 +37,7 @@ description: Changes in Typst 0.11.1
|
|||||||
|
|
||||||
## Export
|
## Export
|
||||||
- Fixed [smart quotes]($smartquote) in PDF outline
|
- Fixed [smart quotes]($smartquote) in PDF outline
|
||||||
- Fixed [patterns]($pattern) with spacing in PDF
|
- Fixed [patterns]($tiling) with spacing in PDF
|
||||||
- Fixed wrong PDF page labels when [page numbering]($page.numbering) was
|
- Fixed wrong PDF page labels when [page numbering]($page.numbering) was
|
||||||
disabled after being previously enabled
|
disabled after being previously enabled
|
||||||
|
|
||||||
|
@ -380,11 +380,11 @@ description: Changes in Typst 0.12.0
|
|||||||
- [`counter.display`] without an established context
|
- [`counter.display`] without an established context
|
||||||
- [`counter.final`] with a location
|
- [`counter.final`] with a location
|
||||||
- [`state.final`] with a location
|
- [`state.final`] with a location
|
||||||
- [`state.display`]
|
- `state.display`
|
||||||
- [`query`] with a location as the second argument
|
- [`query`] with a location as the second argument
|
||||||
- [`locate`] with a callback function
|
- [`locate`] with a callback function
|
||||||
- [`measure`] with styles
|
- [`measure`] with styles
|
||||||
- [`style`]
|
- `style`
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
- Added `typst-kit` crate which provides useful APIs for `World` implementors
|
- Added `typst-kit` crate which provides useful APIs for `World` implementors
|
||||||
|
@ -83,7 +83,7 @@ description: Changes in early, unversioned Typst
|
|||||||
|
|
||||||
- New [`measure`] function
|
- New [`measure`] function
|
||||||
- Measure the layouted size of elements
|
- Measure the layouted size of elements
|
||||||
- To be used in combination with the new [`style`] function that lets you
|
- To be used in combination with the new `style` function that lets you
|
||||||
generate different content based on the style context something is inserted
|
generate different content based on the style context something is inserted
|
||||||
into (because that affects the measured size of content)
|
into (because that affects the measured size of content)
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ applications, while academic applications tend to use strokes instead.
|
|||||||
To add zebra stripes to a table, we use the `table` function's `fill` argument.
|
To add zebra stripes to a table, we use the `table` function's `fill` argument.
|
||||||
It can take three kinds of arguments:
|
It can take three kinds of arguments:
|
||||||
|
|
||||||
- A single color (this can also be a gradient or a pattern) to fill all cells
|
- A single color (this can also be a gradient or a tiling) to fill all cells
|
||||||
with. Because we want some cells to have another color, this is not useful if
|
with. Because we want some cells to have another color, this is not useful if
|
||||||
we want to build zebra tables.
|
we want to build zebra tables.
|
||||||
- An array with colors which Typst cycles through for each column. We can use an
|
- An array with colors which Typst cycles through for each column. We can use an
|
||||||
@ -828,7 +828,7 @@ line appears because there is no `top` line that could suppress it.
|
|||||||
|
|
||||||
### How to achieve a double line? { #double-stroke }
|
### How to achieve a double line? { #double-stroke }
|
||||||
Typst does not yet have a native way to draw double strokes, but there are
|
Typst does not yet have a native way to draw double strokes, but there are
|
||||||
multiple ways to emulate them, for example with [patterns]($pattern). We will
|
multiple ways to emulate them, for example with [tilings]($tiling). We will
|
||||||
show a different workaround in this section: Table gutters.
|
show a different workaround in this section: Table gutters.
|
||||||
|
|
||||||
Tables can space their cells apart using the `gutter` argument. When a gutter is
|
Tables can space their cells apart using the `gutter` argument. When a gutter is
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 380 B |
BIN
tests/ref/counter-page-display.png
Normal file
After Width: | Height: | Size: 150 B |
BIN
tests/ref/enum-numbering-reversed-overriden.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
tests/ref/enum-numbering-reversed.png
Normal file
After Width: | Height: | Size: 620 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
tests/ref/issue-5296-block-sticky-in-block-at-top.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
tests/ref/issue-5296-block-sticky-spaced-from-top-of-page.png
Normal file
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 125 B |
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 128 B |
BIN
tests/ref/tiling-pattern-compatibility.png
Normal file
After Width: | Height: | Size: 125 B |
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 83 B |
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 215 B |
Before Width: | Height: | Size: 757 B After Width: | Height: | Size: 757 B |
Before Width: | Height: | Size: 383 B After Width: | Height: | Size: 383 B |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
@ -37,46 +37,6 @@
|
|||||||
// Error: 11-12 variables from outside the context expression are read-only and cannot be modified
|
// Error: 11-12 variables from outside the context expression are read-only and cannot be modified
|
||||||
#context (i = 1)
|
#context (i = 1)
|
||||||
|
|
||||||
--- context-compatibility-locate ---
|
|
||||||
#let s = state("x", 0)
|
|
||||||
#let compute(expr) = [
|
|
||||||
#s.update(x =>
|
|
||||||
eval(expr.replace("x", str(x)))
|
|
||||||
)
|
|
||||||
// Warning: 17-28 `state.display` is deprecated
|
|
||||||
// Hint: 17-28 use `state.get` in a `context` expression instead
|
|
||||||
New value is #s.display().
|
|
||||||
]
|
|
||||||
|
|
||||||
// Warning: 1:2-6:3 `locate` with callback function is deprecated
|
|
||||||
// Hint: 1:2-6:3 use a `context` expression instead
|
|
||||||
#locate(loc => {
|
|
||||||
// Warning: 14-32 calling `query` with a location is deprecated
|
|
||||||
// Hint: 14-32 try removing the location argument
|
|
||||||
let elem = query(<here>, loc).first()
|
|
||||||
test(s.at(elem.location()), 13)
|
|
||||||
})
|
|
||||||
|
|
||||||
#compute("10") \
|
|
||||||
#compute("x + 3") \
|
|
||||||
*Here.* <here> \
|
|
||||||
#compute("x * 2") \
|
|
||||||
#compute("x - 5")
|
|
||||||
|
|
||||||
--- context-compatibility-styling ---
|
|
||||||
// Warning: 2-53 `style` is deprecated
|
|
||||||
// Hint: 2-53 use a `context` expression instead
|
|
||||||
// Warning: 18-39 calling `measure` with a styles argument is deprecated
|
|
||||||
// Hint: 18-39 try removing the styles argument
|
|
||||||
#style(styles => measure([it], styles).width < 20pt)
|
|
||||||
|
|
||||||
--- context-compatibility-counter-display ---
|
|
||||||
#counter(heading).update(10)
|
|
||||||
|
|
||||||
// Warning: 2-44 `counter.display` without context is deprecated
|
|
||||||
// Hint: 2-44 use it in a `context` expression instead
|
|
||||||
#counter(heading).display(n => test(n, 10))
|
|
||||||
|
|
||||||
--- context-delayed-warning ---
|
--- context-delayed-warning ---
|
||||||
// Ensure that the warning that triggers in the first layout iteration is not
|
// Ensure that the warning that triggers in the first layout iteration is not
|
||||||
// surfaced since it goes away in the second one. Just like errors in show
|
// surfaced since it goes away in the second one. Just like errors in show
|
||||||
|
@ -3,14 +3,6 @@
|
|||||||
#test(type(ltr), direction)
|
#test(type(ltr), direction)
|
||||||
#test(type(10 / 3), float)
|
#test(type(10 / 3), float)
|
||||||
|
|
||||||
--- type-string-compatibility ---
|
|
||||||
#test(type(10), int)
|
|
||||||
#test(type(10), "integer")
|
|
||||||
#test("is " + type(10), "is integer")
|
|
||||||
#test(int in ("integer", "string"), true)
|
|
||||||
#test(int in "integers or strings", true)
|
|
||||||
#test(str in "integers or strings", true)
|
|
||||||
|
|
||||||
--- issue-3110-type-constructor ---
|
--- issue-3110-type-constructor ---
|
||||||
// Let the error message report the type name.
|
// Let the error message report the type name.
|
||||||
// Error: 2-9 type content does not have a constructor
|
// Error: 2-9 type content does not have a constructor
|
||||||
|
@ -102,6 +102,14 @@ B
|
|||||||
#set page(numbering: "1 / 1", margin: (bottom: 20pt))
|
#set page(numbering: "1 / 1", margin: (bottom: 20pt))
|
||||||
#counter(page).update(5)
|
#counter(page).update(5)
|
||||||
|
|
||||||
|
--- counter-page-display ---
|
||||||
|
// Counter display should use numbering from style chain.
|
||||||
|
#set page(
|
||||||
|
numbering: "A",
|
||||||
|
margin: (bottom: 20pt),
|
||||||
|
footer: context align(center, counter(page).display())
|
||||||
|
)
|
||||||
|
|
||||||
--- counter-figure ---
|
--- counter-figure ---
|
||||||
// Count figures.
|
// Count figures.
|
||||||
#figure(numbering: "A", caption: [Four 'A's], kind: image, supplement: "Figure")[_AAAA!_]
|
#figure(numbering: "A", caption: [Four 'A's], kind: image, supplement: "Figure")[_AAAA!_]
|
||||||
|
@ -279,3 +279,28 @@ First!
|
|||||||
// Test box in 100% width block.
|
// Test box in 100% width block.
|
||||||
#block(width: 100%, fill: red, box("a box"))
|
#block(width: 100%, fill: red, box("a box"))
|
||||||
#block(width: 100%, fill: red, [#box("a box") #box()])
|
#block(width: 100%, fill: red, [#box("a box") #box()])
|
||||||
|
|
||||||
|
--- issue-5296-block-sticky-in-block-at-top ---
|
||||||
|
#set page(height: 3cm)
|
||||||
|
#v(1.6cm)
|
||||||
|
#block(height: 2cm, breakable: true)[
|
||||||
|
#block(sticky: true)[*A*]
|
||||||
|
|
||||||
|
b
|
||||||
|
]
|
||||||
|
|
||||||
|
--- issue-5296-block-sticky-spaced-from-top-of-page ---
|
||||||
|
#set page(height: 3cm)
|
||||||
|
#v(2cm)
|
||||||
|
|
||||||
|
#block(sticky: true)[*A*]
|
||||||
|
|
||||||
|
b
|
||||||
|
|
||||||
|
--- issue-5296-block-sticky-weakly-spaced-from-top-of-page ---
|
||||||
|
#set page(height: 3cm)
|
||||||
|
#v(2cm, weak: true)
|
||||||
|
|
||||||
|
#block(sticky: true)[*A*]
|
||||||
|
|
||||||
|
b
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
--- grid-stroke-pattern ---
|
--- grid-stroke-tiling ---
|
||||||
#let double-line = pattern(size: (1.5pt, 1.5pt), {
|
#let double-line = tiling(size: (1.5pt, 1.5pt), {
|
||||||
place(line(stroke: .6pt, start: (0%, 50%), end: (100%, 50%)))
|
place(line(stroke: .6pt, start: (0%, 50%), end: (100%, 50%)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#table(columns: 3, stroke: none, fill: green, [A], [B], [C])
|
#table(columns: 3, stroke: none, fill: green, [A], [B], [C])
|
||||||
|
|
||||||
--- table-fill-bad ---
|
--- table-fill-bad ---
|
||||||
// Error: 14-19 expected color, gradient, pattern, none, array, or function, found string
|
// Error: 14-19 expected color, gradient, tiling, none, array, or function, found string
|
||||||
#table(fill: "hey")
|
#table(fill: "hey")
|
||||||
|
|
||||||
--- table-align-array ---
|
--- table-align-array ---
|
||||||
|
@ -71,6 +71,23 @@ a + 0.
|
|||||||
+ First
|
+ First
|
||||||
+ Nested
|
+ Nested
|
||||||
|
|
||||||
|
--- enum-numbering-reversed ---
|
||||||
|
// Test reverse numbering.
|
||||||
|
#set enum(reversed: true)
|
||||||
|
+ Coffee
|
||||||
|
+ Tea
|
||||||
|
+ Milk
|
||||||
|
|
||||||
|
--- enum-numbering-reversed-overriden ---
|
||||||
|
// Test reverse numbering with overriden numbers.
|
||||||
|
#set enum(reversed: true)
|
||||||
|
+ A
|
||||||
|
+ B
|
||||||
|
+ C
|
||||||
|
9. D
|
||||||
|
+ E
|
||||||
|
+ F
|
||||||
|
|
||||||
--- enum-numbering-closure ---
|
--- enum-numbering-closure ---
|
||||||
// Test numbering with closure.
|
// Test numbering with closure.
|
||||||
#enum(
|
#enum(
|
||||||
|
@ -43,8 +43,6 @@ A
|
|||||||
#set outline(fill: none)
|
#set outline(fill: none)
|
||||||
|
|
||||||
#context test(outline.indent, none)
|
#context test(outline.indent, none)
|
||||||
#outline(indent: false)
|
|
||||||
#outline(indent: true)
|
|
||||||
#outline(indent: none)
|
#outline(indent: none)
|
||||||
#outline(indent: auto)
|
#outline(indent: auto)
|
||||||
#outline(indent: 2em)
|
#outline(indent: 2em)
|
||||||
@ -62,8 +60,6 @@ A
|
|||||||
#show heading: none
|
#show heading: none
|
||||||
#set outline(fill: none)
|
#set outline(fill: none)
|
||||||
|
|
||||||
#outline(indent: false)
|
|
||||||
#outline(indent: true)
|
|
||||||
#outline(indent: none)
|
#outline(indent: none)
|
||||||
#outline(indent: auto)
|
#outline(indent: auto)
|
||||||
#outline(indent: n => 2em * n)
|
#outline(indent: n => 2em * n)
|
||||||
|
@ -13,12 +13,8 @@ fi
|
|||||||
#let c = counter("mycounter")
|
#let c = counter("mycounter")
|
||||||
#c.update(1)
|
#c.update(1)
|
||||||
|
|
||||||
// Warning: 1:2-7:3 `locate` with callback function is deprecated
|
#context [
|
||||||
// Hint: 1:2-7:3 use a `context` expression instead
|
|
||||||
#locate(loc => [
|
|
||||||
#c.update(2)
|
#c.update(2)
|
||||||
#c.at(loc) \
|
#c.get() \
|
||||||
// Warning: 12-36 `locate` with callback function is deprecated
|
Second: #context c.get()
|
||||||
// Hint: 12-36 use a `context` expression instead
|
]
|
||||||
Second: #locate(loc => c.at(loc))
|
|
||||||
])
|
|
||||||
|
@ -49,3 +49,65 @@
|
|||||||
--- symbol-unknown-modifier ---
|
--- symbol-unknown-modifier ---
|
||||||
// Error: 13-20 unknown symbol modifier
|
// Error: 13-20 unknown symbol modifier
|
||||||
#emoji.face.garbage
|
#emoji.face.garbage
|
||||||
|
|
||||||
|
--- symbol-repr ---
|
||||||
|
#test(
|
||||||
|
repr(sym.amp),
|
||||||
|
`symbol("&", ("inv", "⅋"))`.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(sym.amp.inv),
|
||||||
|
`symbol("⅋")`.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(sym.arrow.double.r),
|
||||||
|
```
|
||||||
|
symbol(
|
||||||
|
"⇒",
|
||||||
|
("bar", "⤇"),
|
||||||
|
("long", "⟹"),
|
||||||
|
("long.bar", "⟾"),
|
||||||
|
("not", "⇏"),
|
||||||
|
("l", "⇔"),
|
||||||
|
("l.long", "⟺"),
|
||||||
|
("l.not", "⇎"),
|
||||||
|
)
|
||||||
|
```.text,
|
||||||
|
)
|
||||||
|
#test(repr(sym.smash), "symbol(\"⨳\")")
|
||||||
|
|
||||||
|
#let envelope = symbol(
|
||||||
|
"🖂",
|
||||||
|
("stamped", "🖃"),
|
||||||
|
("stamped.pen", "🖆"),
|
||||||
|
("lightning", "🖄"),
|
||||||
|
("fly", "🖅"),
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(envelope),
|
||||||
|
```
|
||||||
|
symbol(
|
||||||
|
"🖂",
|
||||||
|
("stamped", "🖃"),
|
||||||
|
("stamped.pen", "🖆"),
|
||||||
|
("lightning", "🖄"),
|
||||||
|
("fly", "🖅"),
|
||||||
|
)
|
||||||
|
```.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(envelope.stamped),
|
||||||
|
`symbol("🖃", ("pen", "🖆"))`.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(envelope.stamped.pen),
|
||||||
|
`symbol("🖆")`.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(envelope.lightning),
|
||||||
|
`symbol("🖄")`.text,
|
||||||
|
)
|
||||||
|
#test(
|
||||||
|
repr(envelope.fly),
|
||||||
|
`symbol("🖅")`.text,
|
||||||
|
)
|
||||||
|
@ -84,7 +84,7 @@ I
|
|||||||
|
|
||||||
--- issue-5499-text-fill-in-clip-block ---
|
--- issue-5499-text-fill-in-clip-block ---
|
||||||
|
|
||||||
#let pat = pattern(
|
#let t = tiling(
|
||||||
size: (30pt, 30pt),
|
size: (30pt, 30pt),
|
||||||
relative: "parent",
|
relative: "parent",
|
||||||
square(
|
square(
|
||||||
@ -101,7 +101,7 @@ I
|
|||||||
[ ]
|
[ ]
|
||||||
text(fill: gradient.linear(..color.map.rainbow), "Hello")
|
text(fill: gradient.linear(..color.map.rainbow), "Hello")
|
||||||
[ ]
|
[ ]
|
||||||
text(fill: pat, "Hello")
|
text(fill: t, "Hello")
|
||||||
})
|
})
|
||||||
#block(clip: true, height: 2em, {
|
#block(clip: true, height: 2em, {
|
||||||
text(fill: blue, "Hello")
|
text(fill: blue, "Hello")
|
||||||
@ -110,5 +110,5 @@ I
|
|||||||
[ ]
|
[ ]
|
||||||
text(fill: gradient.linear(..color.map.rainbow), "Hello")
|
text(fill: gradient.linear(..color.map.rainbow), "Hello")
|
||||||
[ ]
|
[ ]
|
||||||
text(fill: pat, "Hello")
|
text(fill: t, "Hello")
|
||||||
})
|
})
|
||||||
|
@ -390,7 +390,7 @@
|
|||||||
--- gradient-text-bad-relative ---
|
--- gradient-text-bad-relative ---
|
||||||
// Make sure they don't work when `relative: "self"`.
|
// Make sure they don't work when `relative: "self"`.
|
||||||
// Hint: 17-61 make sure to set `relative: auto` on your text fill
|
// Hint: 17-61 make sure to set `relative: auto` on your text fill
|
||||||
// Error: 17-61 gradients and patterns on text must be relative to the parent
|
// Error: 17-61 gradients and tilings on text must be relative to the parent
|
||||||
#set text(fill: gradient.linear(red, blue, relative: "self"))
|
#set text(fill: gradient.linear(red, blue, relative: "self"))
|
||||||
|
|
||||||
--- gradient-text-global ---
|
--- gradient-text-global ---
|
||||||
|
@ -1,159 +0,0 @@
|
|||||||
// Test patterns.
|
|
||||||
|
|
||||||
--- pattern-line ---
|
|
||||||
// Tests that simple patterns work.
|
|
||||||
#set page(width: auto, height: auto, margin: 0pt)
|
|
||||||
#let pat = pattern(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
|
||||||
#rect(width: 50pt, height: 50pt, fill: pat)
|
|
||||||
|
|
||||||
--- pattern-lines ---
|
|
||||||
#set page(width: auto, height: auto, margin: 0pt)
|
|
||||||
|
|
||||||
#let pat = pattern(size: (10pt, 10pt), {
|
|
||||||
place(line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
|
||||||
place(line(stroke: 4pt, start: (100%,0%), end: (200%, 100%)))
|
|
||||||
place(line(stroke: 4pt, start: (0%,100%), end: (100%, 200%)))
|
|
||||||
place(line(stroke: 4pt, start: (-100%,0%), end: (0%, 100%)))
|
|
||||||
place(line(stroke: 4pt, start: (0%,-100%), end: (100%, 0%)))
|
|
||||||
})
|
|
||||||
#rect(width: 50pt, height: 50pt, fill: pat)
|
|
||||||
|
|
||||||
--- pattern-relative-self ---
|
|
||||||
// Test with relative set to `"self"`
|
|
||||||
#let pat(..args) = pattern(size: (30pt, 30pt), ..args)[
|
|
||||||
#set line(stroke: green)
|
|
||||||
#place(top + left, line(start: (0%, 0%), end: (100%, 100%), stroke: 1pt))
|
|
||||||
#place(top + left, line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt))
|
|
||||||
]
|
|
||||||
|
|
||||||
#set page(fill: pat(), width: 100pt, height: 100pt)
|
|
||||||
#rect(
|
|
||||||
width: 100%,
|
|
||||||
height: 100%,
|
|
||||||
fill: pat(relative: "self"),
|
|
||||||
stroke: 1pt + green,
|
|
||||||
)
|
|
||||||
|
|
||||||
--- pattern-relative-parent ---
|
|
||||||
// Test with relative set to `"parent"`
|
|
||||||
#let pat(fill, ..args) = pattern(size: (30pt, 30pt), ..args)[
|
|
||||||
#rect(width: 100%, height: 100%, fill: fill, stroke: none)
|
|
||||||
#place(top + left, line(start: (0%, 0%), end: (100%, 100%), stroke: 1pt))
|
|
||||||
#place(top + left, line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt))
|
|
||||||
]
|
|
||||||
|
|
||||||
#set page(fill: pat(white), width: 100pt, height: 100pt)
|
|
||||||
|
|
||||||
#rect(fill: pat(none, relative: "parent"), width: 100%, height: 100%, stroke: 1pt)
|
|
||||||
|
|
||||||
--- pattern-small ---
|
|
||||||
// Tests small patterns for pixel accuracy.
|
|
||||||
#box(
|
|
||||||
width: 8pt,
|
|
||||||
height: 1pt,
|
|
||||||
fill: pattern(size: (1pt, 1pt), square(size: 1pt, fill: black))
|
|
||||||
)
|
|
||||||
#v(-1em)
|
|
||||||
#box(
|
|
||||||
width: 8pt,
|
|
||||||
height: 1pt,
|
|
||||||
fill: pattern(size: (2pt, 1pt), square(size: 1pt, fill: black))
|
|
||||||
)
|
|
||||||
|
|
||||||
--- pattern-zero-sized ---
|
|
||||||
// Error: 15-52 pattern tile size must be non-zero
|
|
||||||
// Hint: 15-52 try setting the size manually
|
|
||||||
#line(stroke: pattern(path((0pt, 0pt), (1em, 0pt))))
|
|
||||||
|
|
||||||
--- pattern-spacing-negative ---
|
|
||||||
// Test with spacing set to `(-10pt, -10pt)`
|
|
||||||
#let pat(..args) = pattern(size: (30pt, 30pt), ..args)[
|
|
||||||
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
|
||||||
]
|
|
||||||
|
|
||||||
#set page(width: 100pt, height: 100pt)
|
|
||||||
|
|
||||||
#rect(fill: pat(spacing: (-10pt, -10pt)), width: 100%, height: 100%, stroke: 1pt)
|
|
||||||
|
|
||||||
--- pattern-spacing-zero ---
|
|
||||||
// Test with spacing set to `(0pt, 0pt)`
|
|
||||||
#let pat(..args) = pattern(size: (30pt, 30pt), ..args)[
|
|
||||||
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
|
||||||
]
|
|
||||||
|
|
||||||
#set page(width: 100pt, height: 100pt)
|
|
||||||
|
|
||||||
#rect(fill: pat(spacing: (0pt, 0pt)), width: 100%, height: 100%, stroke: 1pt)
|
|
||||||
|
|
||||||
--- pattern-spacing-positive ---
|
|
||||||
// Test with spacing set to `(10pt, 10pt)`
|
|
||||||
#let pat(..args) = pattern(size: (30pt, 30pt), ..args)[
|
|
||||||
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
|
||||||
]
|
|
||||||
|
|
||||||
#set page(width: 100pt, height: 100pt)
|
|
||||||
|
|
||||||
#rect(fill: pat(spacing: (10pt, 10pt,)), width: 100%, height: 100%, stroke: 1pt)
|
|
||||||
|
|
||||||
--- pattern-stroke ---
|
|
||||||
// Test pattern on strokes
|
|
||||||
#align(
|
|
||||||
center + top,
|
|
||||||
square(
|
|
||||||
size: 50pt,
|
|
||||||
fill: pattern(
|
|
||||||
size: (5pt, 5pt),
|
|
||||||
align(horizon + center, circle(fill: blue, radius: 2.5pt))
|
|
||||||
),
|
|
||||||
stroke: 7.5pt + pattern(
|
|
||||||
size: (5pt, 5pt),
|
|
||||||
align(horizon + center, circle(fill: red, radius: 2.5pt))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
--- pattern-stroke-relative-parent ---
|
|
||||||
// Test pattern on strokes with relative set to `"parent"`
|
|
||||||
// The pattern on the circle should align with the pattern on the square.
|
|
||||||
#align(
|
|
||||||
center + top,
|
|
||||||
block(
|
|
||||||
width: 50pt,
|
|
||||||
height: 50pt,
|
|
||||||
fill: pattern(size: (5pt, 5pt), circle(radius: 2.5pt, fill: blue)),
|
|
||||||
align(center + horizon, circle(
|
|
||||||
radius: 15pt,
|
|
||||||
stroke: 7.5pt + pattern(
|
|
||||||
size: (5pt, 5pt), circle(radius: 2.5pt, fill: red), relative: "parent"
|
|
||||||
),
|
|
||||||
))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
--- pattern-text ---
|
|
||||||
// Test a pattern on some text
|
|
||||||
// You shouldn't be able to see the text, if you can then
|
|
||||||
// that means that the transform matrices are not being
|
|
||||||
// applied to the text correctly.
|
|
||||||
#let pat = pattern(
|
|
||||||
size: (30pt, 30pt),
|
|
||||||
relative: "parent",
|
|
||||||
square(size: 30pt, fill: gradient.conic(..color.map.rainbow))
|
|
||||||
);
|
|
||||||
|
|
||||||
#set page(
|
|
||||||
width: 140pt,
|
|
||||||
height: 140pt,
|
|
||||||
fill: pat
|
|
||||||
)
|
|
||||||
|
|
||||||
#rotate(45deg, scale(x: 50%, y: 70%, rect(
|
|
||||||
width: 100%,
|
|
||||||
height: 100%,
|
|
||||||
stroke: 1pt,
|
|
||||||
)[
|
|
||||||
#lorem(10)
|
|
||||||
|
|
||||||
#set text(fill: pat)
|
|
||||||
#lorem(10)
|
|
||||||
]))
|
|
@ -55,7 +55,7 @@
|
|||||||
#rect(width: 20pt, height: 20pt, stroke: (thickness: 5pt, join: "round"))
|
#rect(width: 20pt, height: 20pt, stroke: (thickness: 5pt, join: "round"))
|
||||||
|
|
||||||
--- red-stroke-bad-type ---
|
--- red-stroke-bad-type ---
|
||||||
// Error: 15-21 expected length, color, gradient, pattern, dictionary, stroke, none, or auto, found array
|
// Error: 15-21 expected length, color, gradient, tiling, dictionary, stroke, none, or auto, found array
|
||||||
#rect(stroke: (1, 2))
|
#rect(stroke: (1, 2))
|
||||||
|
|
||||||
--- rect-fill-stroke ---
|
--- rect-fill-stroke ---
|
||||||
|
163
tests/suite/visualize/tiling.typ
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// Test tilings.
|
||||||
|
|
||||||
|
--- tiling-line ---
|
||||||
|
// Tests that simple tilings work.
|
||||||
|
#set page(width: auto, height: auto, margin: 0pt)
|
||||||
|
#let t = tiling(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
||||||
|
#rect(width: 50pt, height: 50pt, fill: t)
|
||||||
|
|
||||||
|
--- tiling-lines ---
|
||||||
|
#set page(width: auto, height: auto, margin: 0pt)
|
||||||
|
|
||||||
|
#let t = tiling(size: (10pt, 10pt), {
|
||||||
|
place(line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
||||||
|
place(line(stroke: 4pt, start: (100%,0%), end: (200%, 100%)))
|
||||||
|
place(line(stroke: 4pt, start: (0%,100%), end: (100%, 200%)))
|
||||||
|
place(line(stroke: 4pt, start: (-100%,0%), end: (0%, 100%)))
|
||||||
|
place(line(stroke: 4pt, start: (0%,-100%), end: (100%, 0%)))
|
||||||
|
})
|
||||||
|
#rect(width: 50pt, height: 50pt, fill: t)
|
||||||
|
|
||||||
|
--- tiling-relative-self ---
|
||||||
|
// Test with relative set to `"self"`
|
||||||
|
#let t(..args) = tiling(size: (30pt, 30pt), ..args)[
|
||||||
|
#set line(stroke: green)
|
||||||
|
#place(top + left, line(start: (0%, 0%), end: (100%, 100%), stroke: 1pt))
|
||||||
|
#place(top + left, line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt))
|
||||||
|
]
|
||||||
|
|
||||||
|
#set page(fill: t(), width: 100pt, height: 100pt)
|
||||||
|
#rect(
|
||||||
|
width: 100%,
|
||||||
|
height: 100%,
|
||||||
|
fill: t(relative: "self"),
|
||||||
|
stroke: 1pt + green,
|
||||||
|
)
|
||||||
|
|
||||||
|
--- tiling-relative-parent ---
|
||||||
|
// Test with relative set to `"parent"`
|
||||||
|
#let t(fill, ..args) = tiling(size: (30pt, 30pt), ..args)[
|
||||||
|
#rect(width: 100%, height: 100%, fill: fill, stroke: none)
|
||||||
|
#place(top + left, line(start: (0%, 0%), end: (100%, 100%), stroke: 1pt))
|
||||||
|
#place(top + left, line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt))
|
||||||
|
]
|
||||||
|
|
||||||
|
#set page(fill: t(white), width: 100pt, height: 100pt)
|
||||||
|
|
||||||
|
#rect(fill: t(none, relative: "parent"), width: 100%, height: 100%, stroke: 1pt)
|
||||||
|
|
||||||
|
--- tiling-small ---
|
||||||
|
// Tests small tilings for pixel accuracy.
|
||||||
|
#box(
|
||||||
|
width: 8pt,
|
||||||
|
height: 1pt,
|
||||||
|
fill: tiling(size: (1pt, 1pt), square(size: 1pt, fill: black))
|
||||||
|
)
|
||||||
|
#v(-1em)
|
||||||
|
#box(
|
||||||
|
width: 8pt,
|
||||||
|
height: 1pt,
|
||||||
|
fill: tiling(size: (2pt, 1pt), square(size: 1pt, fill: black))
|
||||||
|
)
|
||||||
|
|
||||||
|
--- tiling-zero-sized ---
|
||||||
|
// Error: 15-51 tile size must be non-zero
|
||||||
|
// Hint: 15-51 try setting the size manually
|
||||||
|
#line(stroke: tiling(path((0pt, 0pt), (1em, 0pt))))
|
||||||
|
|
||||||
|
--- tiling-spacing-negative ---
|
||||||
|
// Test with spacing set to `(-10pt, -10pt)`
|
||||||
|
#let t(..args) = tiling(size: (30pt, 30pt), ..args)[
|
||||||
|
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
||||||
|
]
|
||||||
|
|
||||||
|
#set page(width: 100pt, height: 100pt)
|
||||||
|
|
||||||
|
#rect(fill: t(spacing: (-10pt, -10pt)), width: 100%, height: 100%, stroke: 1pt)
|
||||||
|
|
||||||
|
--- tiling-spacing-zero ---
|
||||||
|
// Test with spacing set to `(0pt, 0pt)`
|
||||||
|
#let t(..args) = tiling(size: (30pt, 30pt), ..args)[
|
||||||
|
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
||||||
|
]
|
||||||
|
|
||||||
|
#set page(width: 100pt, height: 100pt)
|
||||||
|
|
||||||
|
#rect(fill: t(spacing: (0pt, 0pt)), width: 100%, height: 100%, stroke: 1pt)
|
||||||
|
|
||||||
|
--- tiling-spacing-positive ---
|
||||||
|
// Test with spacing set to `(10pt, 10pt)`
|
||||||
|
#let t(..args) = tiling(size: (30pt, 30pt), ..args)[
|
||||||
|
#square(width: 100%, height: 100%, stroke: 1pt, fill: blue)
|
||||||
|
]
|
||||||
|
|
||||||
|
#set page(width: 100pt, height: 100pt)
|
||||||
|
|
||||||
|
#rect(fill: t(spacing: (10pt, 10pt,)), width: 100%, height: 100%, stroke: 1pt)
|
||||||
|
|
||||||
|
--- tiling-stroke ---
|
||||||
|
// Test tiling on strokes
|
||||||
|
#align(
|
||||||
|
center + top,
|
||||||
|
square(
|
||||||
|
size: 50pt,
|
||||||
|
fill: tiling(
|
||||||
|
size: (5pt, 5pt),
|
||||||
|
align(horizon + center, circle(fill: blue, radius: 2.5pt))
|
||||||
|
),
|
||||||
|
stroke: 7.5pt + tiling(
|
||||||
|
size: (5pt, 5pt),
|
||||||
|
align(horizon + center, circle(fill: red, radius: 2.5pt))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
--- tiling-stroke-relative-parent ---
|
||||||
|
// Test tiling on strokes with relative set to `"parent"`
|
||||||
|
// The tiling on the circle should align with the tiling on the square.
|
||||||
|
#align(
|
||||||
|
center + top,
|
||||||
|
block(
|
||||||
|
width: 50pt,
|
||||||
|
height: 50pt,
|
||||||
|
fill: tiling(size: (5pt, 5pt), circle(radius: 2.5pt, fill: blue)),
|
||||||
|
align(center + horizon, circle(
|
||||||
|
radius: 15pt,
|
||||||
|
stroke: 7.5pt + tiling(
|
||||||
|
size: (5pt, 5pt), circle(radius: 2.5pt, fill: red), relative: "parent"
|
||||||
|
),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
--- tiling-text ---
|
||||||
|
// Test a tiling on some text. You shouldn't be able to see the text, if you can
|
||||||
|
// then that means that the transform matrices are not being applied to the text
|
||||||
|
// correctly.
|
||||||
|
#let t = tiling(
|
||||||
|
size: (30pt, 30pt),
|
||||||
|
relative: "parent",
|
||||||
|
square(size: 30pt, fill: gradient.conic(..color.map.rainbow))
|
||||||
|
);
|
||||||
|
|
||||||
|
#set page(
|
||||||
|
width: 140pt,
|
||||||
|
height: 140pt,
|
||||||
|
fill: t
|
||||||
|
)
|
||||||
|
|
||||||
|
#rotate(45deg, scale(x: 50%, y: 70%, rect(
|
||||||
|
width: 100%,
|
||||||
|
height: 100%,
|
||||||
|
stroke: 1pt,
|
||||||
|
)[
|
||||||
|
#lorem(10)
|
||||||
|
|
||||||
|
#set text(fill: t)
|
||||||
|
#lorem(10)
|
||||||
|
]))
|
||||||
|
|
||||||
|
--- tiling-pattern-compatibility ---
|
||||||
|
#set page(width: auto, height: auto, margin: 0pt)
|
||||||
|
#let t = pattern(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
||||||
|
#rect(width: 50pt, height: 50pt, fill: t)
|