From 1a390deaea040191cf0e5937bd8e1427b49db71b Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 11 Mar 2023 20:01:56 +0100 Subject: [PATCH] Figures --- library/src/layout/spacing.rs | 6 ++ library/src/layout/table.rs | 12 +++- library/src/lib.rs | 1 + library/src/meta/figure.rs | 120 +++++++++++++++++++++++++++++++++ library/src/meta/heading.rs | 35 +++++----- library/src/meta/mod.rs | 2 + library/src/meta/outline.rs | 18 +++-- library/src/meta/reference.rs | 28 ++++---- library/src/visualize/image.rs | 12 ++-- src/eval/func.rs | 27 ++++---- src/model/content.rs | 13 +++- tests/ref/meta/figure.png | Bin 0 -> 25146 bytes tests/typ/meta/figure.typ | 24 +++++++ 13 files changed, 243 insertions(+), 55 deletions(-) create mode 100644 library/src/meta/figure.rs create mode 100644 tests/ref/meta/figure.png create mode 100644 tests/typ/meta/figure.typ diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs index dbdf0c11e..e67fec03d 100644 --- a/library/src/layout/spacing.rs +++ b/library/src/layout/spacing.rs @@ -191,6 +191,12 @@ impl From for Spacing { } } +impl From for Spacing { + fn from(length: Length) -> Self { + Self::Rel(length.into()) + } +} + impl From for Spacing { fn from(fr: Fr) -> Self { Self::Fr(fr) diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs index fabe8c332..012e63acc 100644 --- a/library/src/layout/table.rs +++ b/library/src/layout/table.rs @@ -1,4 +1,5 @@ use crate::layout::{AlignNode, GridLayouter, TrackSizings}; +use crate::meta::LocalName; use crate::prelude::*; /// A table of items. @@ -31,7 +32,7 @@ use crate::prelude::*; /// /// Display: Table /// Category: layout -#[node(Layout)] +#[node(Layout, LocalName)] pub struct TableNode { /// Defines the column sizes. See the [grid documentation]($func/grid) for /// more information on track sizing. @@ -264,3 +265,12 @@ impl> From> for Value { } } } + +impl LocalName for TableNode { + fn local_name(&self, lang: Lang) -> &'static str { + match lang { + Lang::GERMAN => "Tabelle", + Lang::ENGLISH | _ => "Table", + } + } +} diff --git a/library/src/lib.rs b/library/src/lib.rs index 36afdb2be..35cae5265 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -88,6 +88,7 @@ fn global(math: Module, calc: Module) -> Module { global.define("link", meta::LinkNode::id()); global.define("outline", meta::OutlineNode::id()); global.define("heading", meta::HeadingNode::id()); + global.define("figure", meta::FigureNode::id()); global.define("numbering", meta::numbering); // Symbols. diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs new file mode 100644 index 000000000..6c43fa686 --- /dev/null +++ b/library/src/meta/figure.rs @@ -0,0 +1,120 @@ +use std::str::FromStr; + +use super::{LocalName, Numbering, NumberingPattern}; +use crate::layout::{BlockNode, TableNode, VNode}; +use crate::prelude::*; +use crate::text::TextNode; + +/// A figure with an optional caption. +/// +/// ## Example +/// ```example +/// = Pipeline +/// @fig-lab shows the central step of +/// our molecular testing pipeline. +/// +/// #figure( +/// image("molecular.jpg", width: 80%), +/// caption: [ +/// The molecular testing pipeline. +/// ], +/// ) +/// ``` +/// +/// Display: Figure +/// Category: meta +#[node(Synthesize, Show, LocalName)] +pub struct FigureNode { + /// The content of the figure. Often, an [image]($func/image). + #[required] + pub body: Content, + + /// The figure's caption. + pub caption: Option, + + /// How to number the figure. Accepts a + /// [numbering pattern or function]($func/numbering). + #[default(Some(Numbering::Pattern(NumberingPattern::from_str("1").unwrap())))] + pub numbering: Option, + + /// The vertical gap between the body and caption. + #[default(Em::new(0.65).into())] + pub gap: Length, + + /// The figure's number. + #[synthesized] + pub number: Option, +} + +impl FigureNode { + fn element(&self) -> NodeId { + let mut id = self.body().id(); + if id != NodeId::of::() { + id = NodeId::of::(); + } + id + } +} + +impl Synthesize for FigureNode { + fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content { + let my_id = vt.identify(self); + let element = self.element(); + + let numbering = self.numbering(styles); + let mut number = None; + if numbering.is_some() { + number = NonZeroUsize::new( + 1 + vt + .locate(Selector::node::()) + .into_iter() + .take_while(|&(id, _)| id != my_id) + .filter(|(_, node)| node.to::().unwrap().element() == element) + .count(), + ); + } + + let node = self.clone().with_number(number).with_numbering(numbering).pack(); + let meta = Meta::Node(my_id, node.clone()); + node.styled(MetaNode::set_data(vec![meta])) + } +} + +impl Show for FigureNode { + fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult { + let mut realized = self.body(); + + if let Some(mut caption) = self.caption(styles) { + if let Some(numbering) = self.numbering(styles) { + let number = self.number().unwrap(); + let name = self.local_name(TextNode::lang_in(styles)); + caption = TextNode::packed(eco_format!("{name}\u{a0}")) + + numbering.apply(vt.world(), &[number])?.display() + + TextNode::packed(": ") + + caption; + } + + realized += VNode::weak(self.gap(styles).into()).pack(); + realized += caption; + } + + Ok(BlockNode::new() + .with_body(Some(realized)) + .pack() + .aligned(Axes::with_x(Some(Align::Center.into())))) + } +} + +impl LocalName for FigureNode { + fn local_name(&self, lang: Lang) -> &'static str { + let body = self.body(); + if body.is::() { + return body.with::().unwrap().local_name(lang); + } + + match lang { + Lang::GERMAN => "Abbildung", + Lang::ENGLISH | _ => "Figure", + } + } +} diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs index 1bff3af4e..e82f80c6c 100644 --- a/library/src/meta/heading.rs +++ b/library/src/meta/heading.rs @@ -1,6 +1,6 @@ use typst::font::FontWeight; -use super::Numbering; +use super::{LocalName, Numbering}; use crate::layout::{BlockNode, HNode, VNode}; use crate::prelude::*; use crate::text::{TextNode, TextSize}; @@ -40,7 +40,7 @@ use crate::text::{TextNode, TextSize}; /// /// Display: Heading /// Category: meta -#[node(Synthesize, Show, Finalize)] +#[node(Synthesize, Show, Finalize, LocalName)] pub struct HeadingNode { /// The logical nesting depth of the heading, starting from one. #[default(NonZeroUsize::new(1).unwrap())] @@ -78,14 +78,6 @@ pub struct HeadingNode { pub body: Content, /// The heading's numbering numbers. - /// - /// ```example - /// #show heading: it => it.numbers - /// - /// = First - /// == Second - /// = Third - /// ``` #[synthesized] pub numbers: Option>, } @@ -93,17 +85,17 @@ pub struct HeadingNode { impl Synthesize for HeadingNode { fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content { let my_id = vt.identify(self); - let numbered = self.numbering(styles).is_some(); + let numbering = self.numbering(styles); let mut counter = HeadingCounter::new(); - if numbered { - // Advance passed existing headings. + if numbering.is_some() { + // Advance past existing headings. for (_, node) in vt - .locate(Selector::node::()) + .locate(Selector::node::()) .into_iter() .take_while(|&(id, _)| id != my_id) { - let heading = node.to::().unwrap(); + let heading = node.to::().unwrap(); if heading.numbering(StyleChain::default()).is_some() { counter.advance(heading); } @@ -116,8 +108,8 @@ impl Synthesize for HeadingNode { let node = self .clone() .with_outlined(self.outlined(styles)) - .with_numbering(self.numbering(styles)) - .with_numbers(numbered.then(|| counter.take())) + .with_numbers(numbering.is_some().then(|| counter.take())) + .with_numbering(numbering) .pack(); let meta = Meta::Node(my_id, node.clone()); @@ -196,3 +188,12 @@ cast_from_value! { HeadingNode, v: Content => v.to::().ok_or("expected heading")?.clone(), } + +impl LocalName for HeadingNode { + fn local_name(&self, lang: Lang) -> &'static str { + match lang { + Lang::GERMAN => "Abschnitt", + Lang::ENGLISH | _ => "Section", + } + } +} diff --git a/library/src/meta/mod.rs b/library/src/meta/mod.rs index 07f449a42..f5b9bf2f0 100644 --- a/library/src/meta/mod.rs +++ b/library/src/meta/mod.rs @@ -1,6 +1,7 @@ //! Interaction between document parts. mod document; +mod figure; mod heading; mod link; mod numbering; @@ -8,6 +9,7 @@ mod outline; mod reference; pub use self::document::*; +pub use self::figure::*; pub use self::heading::*; pub use self::link::*; pub use self::numbering::*; diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs index a2b125110..81785212c 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -1,4 +1,4 @@ -use super::HeadingNode; +use super::{HeadingNode, LocalName}; use crate::layout::{BoxNode, HNode, HideNode, ParbreakNode, RepeatNode}; use crate::prelude::*; use crate::text::{LinebreakNode, SpaceNode, TextNode}; @@ -22,7 +22,7 @@ use crate::text::{LinebreakNode, SpaceNode, TextNode}; /// /// Display: Outline /// Category: meta -#[node(Synthesize, Show)] +#[node(Synthesize, Show, LocalName)] pub struct OutlineNode { /// The title of the outline. /// @@ -91,10 +91,7 @@ impl Show for OutlineNode { let mut seq = vec![ParbreakNode::new().pack()]; if let Some(title) = self.title(styles) { let title = title.clone().unwrap_or_else(|| { - TextNode::packed(match TextNode::lang_in(styles) { - Lang::GERMAN => "Inhaltsverzeichnis", - Lang::ENGLISH | _ => "Contents", - }) + TextNode::packed(self.local_name(TextNode::lang_in(styles))) }); seq.push( @@ -187,3 +184,12 @@ impl Show for OutlineNode { Ok(Content::sequence(seq)) } } + +impl LocalName for OutlineNode { + fn local_name(&self, lang: Lang) -> &'static str { + match lang { + Lang::GERMAN => "Inhaltsverzeichnis", + Lang::ENGLISH | _ => "Contents", + } + } +} diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index a46198bde..55f2fc6e4 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -1,4 +1,4 @@ -use super::{HeadingNode, Numbering}; +use super::{FigureNode, HeadingNode, Numbering}; use crate::prelude::*; use crate::text::TextNode; @@ -92,7 +92,9 @@ impl Show for RefNode { }; let mut prefix = match self.prefix(styles) { - Smart::Auto => prefix(target, TextNode::lang_in(styles)) + Smart::Auto => target + .with::() + .map(|node| node.local_name(TextNode::lang_in(styles))) .map(TextNode::packed) .unwrap_or_default(), Smart::Custom(None) => Content::empty(), @@ -113,6 +115,13 @@ impl Show for RefNode { } else { bail!(self.span(), "cannot reference unnumbered heading"); } + } else if let Some(figure) = target.to::() { + if let Some(numbering) = figure.numbering(StyleChain::default()) { + let number = figure.number().unwrap(); + numbered(vt, prefix, &numbering, &[number])? + } else { + bail!(self.span(), "cannot reference unnumbered figure"); + } } else { bail!(self.span(), "cannot reference {}", target.id().name); }; @@ -138,15 +147,8 @@ fn numbered( }) } -/// The default prefix. -fn prefix(node: &Content, lang: Lang) -> Option<&str> { - if node.is::() { - match lang { - Lang::ENGLISH => Some("Section"), - Lang::GERMAN => Some("Abschnitt"), - _ => None, - } - } else { - None - } +/// The named with which an element is referenced. +pub trait LocalName { + /// Get the name in the given language. + fn local_name(&self, lang: Lang) -> &'static str; } diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs index 78f477f69..29a04c96b 100644 --- a/library/src/visualize/image.rs +++ b/library/src/visualize/image.rs @@ -11,11 +11,13 @@ use crate::prelude::*; /// /// ## Example /// ```example -/// #align(center)[ -/// #image("molecular.jpg", width: 80%) -/// *A step in the molecular testing -/// pipeline of our lab* -/// ] +/// #figure( +/// image("molecular.jpg", width: 80%), +/// caption: [ +/// A step in the molecular testing +/// pipeline of our lab. +/// ], +/// ) /// ``` /// /// Display: Image diff --git a/src/eval/func.rs b/src/eval/func.rs index 8da5c6bc1..268542402 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -142,6 +142,14 @@ impl Func { self.select(Some(fields)) } + /// The node id of this function if it is an element function. + pub fn id(&self) -> Option { + match **self.0 { + Repr::Node(id) => Some(id), + _ => None, + } + } + /// Execute the function's set rule and return the resulting style map. pub fn set(&self, mut args: Args) -> SourceResult { Ok(match &**self.0 { @@ -156,16 +164,15 @@ impl Func { /// Create a selector for this function's node type. pub fn select(&self, fields: Option) -> StrResult { - match **self.0 { - Repr::Node(id) => { - if id == item!(text_id) { - Err("to select text, please use a string or regex instead")?; - } + let Some(id) = self.id() else { + return Err("this function is not selectable".into()); + }; - Ok(Selector::Node(id, fields)) - } - _ => Err("this function is not selectable")?, + if id == item!(text_id) { + Err("to select text, please use a string or regex instead")?; } + + Ok(Selector::Node(id, fields)) } } @@ -196,10 +203,6 @@ impl From for Func { } } -cast_to_value! { - v: NodeId => Value::Func(v.into()) -} - /// A native Rust function. pub struct NativeFunc { /// The function's implementation. diff --git a/src/model/content.rs b/src/model/content.rs index be7373314..012ad05f9 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -10,7 +10,9 @@ use once_cell::sync::Lazy; use super::{node, Guard, Recipe, Style, StyleMap}; use crate::diag::{SourceResult, StrResult}; -use crate::eval::{cast_from_value, Args, Cast, FuncInfo, Str, Value, Vm}; +use crate::eval::{ + cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm, +}; use crate::syntax::Span; use crate::util::pretty_array_like; use crate::World; @@ -382,6 +384,15 @@ impl Deref for NodeId { } } +cast_from_value! { + NodeId, + v: Func => v.id().ok_or("this function is not an element")? +} + +cast_to_value! { + v: NodeId => Value::Func(v.into()) +} + /// Static node for a node. pub struct NodeMeta { /// The node's name. diff --git a/tests/ref/meta/figure.png b/tests/ref/meta/figure.png new file mode 100644 index 0000000000000000000000000000000000000000..b39f462fa4b01750a320144aa6098483f665035a GIT binary patch literal 25146 zcmb@ucRZGV`vzX3Xy}eI%E&B5h(u;~5tT}kk=x!Yd!(d6cJ@e=7P86AR%8`r7a3)f z9lzu5^E}Vz`+UF8^ZWht>-8dT_qeY2b-vH@IFI8vuX|_Jly+?0yLH2c4LeRL%b(w{ zVWSMb<|)bWNZ_@KhZ{B+Y&<1@@`6j}SiI}iGfnG@!$Ma1zB$2Zvf+=jaTYI$Tn@D+`i$TU;72{%YK0k|NOd9j$dw+ zZ}{^?b_+k?`TzW)-2OjbylP=)X<2OhwIYyN{Hc0k??iNDcsTjy&DNbcW<77Nm#rt= zeegiiaU}TR!;7-AWLFvDPxnU)nV4HxsK<$()6ft)aU!PC*u>D#)ZAQcRgXo|P5ykT z`@)Cj=HP}nacAp0qOVFzOCLXe+*9PdzK6`_OBMyuQeS^+h4s#soh+(R0vp*J7bbd~ znmlUL%Y+Xwn40ag_oxYob)22A9E+X%lnt*NJ zo1)5X-Jd^;8ok>cFkK&ZV6>sQSb~R#Ctx~W+&SuAY)njeczAo7wx+gr*E#D4H8nLQ z?h9)Q(f;@DMaRUf`MLKrHxqX4dK>5P<0FsnSyk1Boqc5Y{r$DC%I_X$Yt4@hcB%TV zUH_clrq`>$V!^8=fo6r8sqq8P_N`PVRFpQ;Q_CF`A3pGS%!xk7bC0u4M|~c|F@HFF zyVUL1p_I?bN$1E--N_l#n8~VNcD^`$L zV^h$97`T>sxk1dw$A>cM^XJd3b&a*PTiE8tzGSzP<8#7U(|M1b4SfDiZzV`H{W@D8 zYweR&>iN*7M{g^i+&LIL8Go{WdD-pUxpP>Ap@ybJ>Dj5Nk)J<>OjxA7JeFp@GrzQ( zG1+QqY}}r#6f{IuTV4HI#yTM(fih`qY)pjw(q1v+^8Kz)4@7HGq=c^|G`-ifSrhaz z@(T=pxbKTAxpDTF`T2PU;?Nf^ve}uLh=_>R=+4eg^+c&BVM|L(D+`m(&dz1_-%opb zdhS~)bD6GaYjeQIuFQ`U+pCTW3FTXU`8G1bKtm(zaFCzB%yG1J`Pa|-`R*d;rk)

zt7xG~x?1Yt`b*Qp(=*zAVYDq3>+zUZk$ z<&g)+*@3f_=D^XhYy!qAt z_LgH`7o8~YH^r;6SDe(Y8O->X_rKn;FF#JyHR6cpujQVTeYD%?)2`$h=!`5KwrIDS z=<!(v~=ZmWkSu%2Mj&$m^WOHR$4 zes0lG#3H^+HM@l1eZ{b zkdTmsgd_a?>YIqqpFh`ls(O-gyt~k0wDrmCaFerx!_U?y+h#jr?}Q5>ej7W z@xlPY%-o#ixqgJ!xnxBOa&kjMLtdPsg#ovTn)u<5_C4B8_j~Ui|hG-#duQV!NF@4i3ZcbO+<_8LP->QEancj^(D&annwfy-W@5Zl<*5YRMubx<$ z5{o!_xP%UJUX_2f9%^L4Qv2j;q0@1}rE0R3hsC@}uCtM$RrHk{M&A`@vV0S`B~6C% zEEd-8ozQUq#Udbo>0U~Xc0q;VoA2?@YNb|ItKCkyjZ4mLq3Ztl@ngPCpU5nVETfE|j?{SL~fT zcfv)!efxG;Gd(4g7_e>Iw&%f=No&7XQlC5t5yS41p)B!UFRdQW&&q23_%S66&uwpS zZ=|p`H{UB{91s>pYb8E4JsmPEV`gT?FrX57Bt(IZj;_*gM>1n?p~Dt7PCh;zU0tpBkN%+8W|Z0dmlTN`{Kn5y8J?=AQt=^Tq*TjIdZk2+&H_;V~L%M>-yMdLARol zlp)IUIyybCuT?rtt|gY^Gk9uThCjsf8Czew#&+mXXYtmwj12YTP37e`SfcZs``*9b z#ugqM+vT+9jeAh0MbR#o$?e@}BIr^;tpFA4fl>ITZj zmo}av9&^5?ZueYTSzjb}yubej!EI)cDz>rI_Up}u5BE@gMHUJpbEvwz>%!}cFE35c z%vgWPs{ZupdfU@8*~X0(4DRmk`OoNPPa`y!=SFFl5`u$e_@BOhEf64>wtVU0MK<4O zy_OAVS#jErpmK{PNR$Rdea<@?k>2w=Gr987R~ZxI+5*In+jiB;OG zAO;ATi;IQL zduk@UQ_C9;%^`@fP@$8@trfD#eVxaa{Z)@><@rT&g@=cp;?}w9JeB3kBHSP!mdj=zJ+38U=L_rxXlQA_SUTC-3dkN56&2;;GMX8z7oM)VM~gZWi<4tzWyJ!~ z(#}qOt)z=JL^i_WrTrd3uIfnF5%*knBoc`Mf>x3=KF6)Q>t5aA@KtsQyoG(R-}ghD z*lgqe_5?|Hf-GV!IrjHR3x~1g5KzSIP-Co^!_%0Uy){woI`_)ozJ1%b-Jrg%Zaa%a z!Sm<$cGt~Oe~NQon7DWUzCl235UcvnqyBV!6%5i|t5L4{eI@R-#@WZMI(-Rm>;^VF zoU^d=X(u=C<-hdm@>{#$7;pgYq8jgKOMn*R0*RvT*!lX%)ZNQEF_Wj)+8lI(VJ{;`q1lb0ff@3~C zJw3Z*kzA|=cT0ON&rJ{10^q&=VtM@dao^^hlpg)%8-1HUz0@2S7|=|+@@f7p4?llj zo>gpA6vNJ)O-Sg-_AAykCML!p!0hTB6swzpy>G4`-f{gXMf$5(@{}z#=3Qha#>VWv+js2Hrj07C z+qHYQFX2j_rPiz=u8@&#-{97#--l8439=_oo@BE4HQp5|*MI;1RyMYzd$b2)qN7W^ ze!t1B3=Ijf`@MX9Z^Q7w00+Nsuy#ODkoxS!y0mKuhd!RyzyS_E(fszjdZkQk@`tur zO-k)r#KKrnz7my<9}pAxP57QSImZ^ZkW8x>u-AR^WO0+!dgWRwO_-hlZJA4S6AeLE zzbrI)M_k-;C=#_@;($|dyvAp_l#I*#^#SyS-Z|x?qoWK1Y;u0VVefeRl!XGY0d5Jh zh}!Od-V-9mSNeUn|LvxL%%@L>BKh@{T`WI;gc)k*TURr*nNJltPhwL? z9x+m^-X#mbZDIF*$BrF>HKZuFv)s$t`svf2-Mcy6FI>F%DML?MN~*N3PI>w6$lPd~ zrHDJ;lalTH+Q;^G)Jk^W`}gmU6dj>Vy0vAe{i-!~)qyiHRf1tTYN=}Roi#~b%X2(t z?zDb8S=9nY1S`2tJTP6p1V_cWa)!}^!zwQ24eQ&Earb-ufLJG}L& z&~UR@m-Dj3dzT)AwQruA?=LWZrlJ7AJ$?E#+LZ>e+}vDtcJ}G%X(pacWMt$N6t{hS zRaI4Ua&ls}xJ3CdZrr$$ii%1@1A9!1T~$jfIxa5m_3J5M4wI&Y@zK%x%1RD?ercQD z;*k+kNl8iHVC)UMAwc^pAFnG}!TfQ>kOFY~tl`1Dfc^y!frPHt|4fKkcE3c%!oHHR;~ zqNJkA%*|yMv1IU!xsJMGXJ>~K1X2PHN2$89;5s)F0z~LvjWGGt))p&dLS4uwGF%tJ z!7%Xl))w_B0X89_S^PS<;l;CO!7HyKBQdM?`@{1Sm zJZjdYH7lNU1#3J}S@q9&~rNU>Kvdw;CCp@G+&%UKiJML_z7 zZ;?_BrfF34%*>uY+xa>#n?FF8(?e<>f3IK#9F2{PzGHi3=#{CxFvtGv$k5Ap_Uz!H zL#F{b)z#HiRNR+lxMCYWG&Y{WKRj~gj3*Y>LcQYX(W6In-pJ?Y<>iU+@QmWx;PpV1 zA2XMimj?$2-$(Lmn3$S*jCbaajEtaZefsn%S}P?brQ^qr0YE(gXg4%WdGe&?ga5|7vPp@QsJUTjhaKqD-lwfDg1ZnSthzP}@;9;_~aVbwG7vzk0@7@hb zUpRYK<^z2_-+=>n3d0k`XB-9@-&Wk-w!ZuhYlGJ7sBr@W;@Eh+e`$7j*REZhFCRZ< zU#1)W*^v=Fe3N6V#@VyBIJX1p2|4qpWp#B~zA)JHwbc5=E9v(Si=kKn4)%-Y_#vNR zZN!XGlEc0j{II=I9`9RIqv+}Rn@;7>jgMCaYLH8YqHHWIy1LB?42#29{iDLd;SV2L zvHT>9IxdPJ=HlX7)@SGEw?U_dV`6qIlj)~c*VRq%@p;=@;{Nq)&KWniH#lqToxs4r z2M-$kcI><2@s^r=^M(!o_#!ai2Li)}e}2jI_uarM*VfdqNVo*AR_8M>O?{Pfbj(4> z`WbQtzB7KikJ3Y3wmP;B#7p7d}yd<`n>b7U-29uZ*R@IiA+B$+ldoy->U9W zCh=+K3G2Dd%oSn}d^nmPO|-Hh78$PH{O+_(^K&O(c0&H=)^{J!0=OMmXkXKF8obO2*=}vovT^3nv`ux9!yfxu)TVR%7K2pTMGWt6*an<&_-QG7=b#%6e!XvpHKd)c# zS0NgNxoXKC))fEE=c8BLP)6q(wA%9#ZEV#beUs4a5xo+QUX%SQGM_CblTyMP`mP2r zi#zT2t@F-XQ$V}YqqxAE(w2pAShZby@ZbTH_r11HpB{#XTN3x|+4B&MP2PMyC!yez z#%+T6^mSR!%25DN4OhosXRs_C?2{q&1-`-*T)85K+qO~-8&NGWi}ieKO$k@kjR>yX zJIp#V)Kym%$F)ChN;v*QORI<>6=j$gjkkl(!en2cq8{V==QF8@YDj@13QCxGUuTKsA8b5iZ~kh(DUmC5SFhS>Bz}#k~KwmGLh}vD@@s0zAlG+ zAunGQ!l9HP;aaik;Ogb&b@nV_R5`7nKvY6PV*ma}OKCZe9`&OGR|;e*bR0czLFQm8 zgUg&tWqAJZ9=2J znCBM1cyZ{^p&uWgYour-m6w-eFSI?WudfGdb?@H220gU=SeX9)eiXi$j2bk4b#-;( zdP$x%)m2s9Yg^O2=y&gqlkz-q@ZjAqW@^CV1_s}fheW1On?r|0J8 zrc8R4VY?ceIE6$d?mW@V@)5mHhHlB}Wh(X}P-=Zw4nM-dA3C&^O&F+thStzD{kcXe zo)o6C05WE6ZLMLg6@VfvEDSACL&G6S$)2XBNl-a0EiGx++k4n30)v8ZZZ2ofo|TvP zef5gBCNcZ*&SUt8^=Msv`Qk;4uo+E_3tAh%5d#AQ24d^R=9iio zHG#}O$HzTEkpPkuIt+_U&il^sy#I6c=5b=k!NdT-_frdULdFf30hFLK}%KDVCxf=ZS+URK+75%8=pDz;MT28{QUeiQS-tC;6QF}5$o=PJ3CoJ zbys$5-;NFEguH=J>|_ltEAyW0E!icD0)OSTz7Gx%rA)T2vGD=t;?ImS#UPdkgwDA= zoqF_Kf`aExp4`Z0NF>Uw^EXykA9EV}?Ca}mH&CN>dVWh4QR!b-84Wt zw?*)2-*wkWRb!TLnF617Ojx*~u@PKxLuI9$xL3@H>xYgX&wuo2f1?RX5~?Y-l7!<3 zL0Z~dUtfQC*JTBT+kqiSX!ixV<6}eYZ$(Dtqj&?)7*aFtj^q|as35e>np870Go?9R z`$d$_r|=A}+zT^7#@ZEWBS@iJ)ca6XD&W*^nh7DNVl{sfc>eZ zrR9YS7dSoHLiW>+3p=;J(rQRAZi*MbLjVu6eY`{Q+_{iw{V!QYR(if5x|pu(^YNwO zilu91`|VCeLQ|r&bF8UJ+EV+>4eM%@d6}A;Iz=csE{-WcP?FbDL|a`w$mvzr=g$WZ z9#j_ZMtgO=NP2YjI0g02ov9pAoCgjN1EM`S94C9@1Jh+`gp|(hqNmT#&R$6nnOdOq z_%`_EaK}XGr1NAoI(&%|>a#vtWEBk}ucNCaB5nwbdM3*0JE(Led^>;Vw>q<^<7&ol zr8*mCNtTaC$odK$?#!2+T8`9L>;V+M%uAOYHBAQzzjoZ{@# ztcmgSwRkcihyCId@O$$?5r?lHo_G9X|01D_8bCp5M}0j(r??L?w`*F9c80M@4U9wCt&*S!5gRXHwM-BZRWRpeoJ2JJedGwm7VQ?F3HJh zeqr0{U_EW%`-KA(r3@@QLXK77L(vIWai!$)O~qV~)jsUAGS4b;!RsUcec9BMl)Crt z`L*-LfV5G4`*uSUiTZNNt&89j?dfdYmA8ZKW3f=j5E*S-o=soxm7x^96|}j=CMIEY zJVww6kQ|bdk`NiF9%CM4VYJ5`hE%Pst-s}=*|5OMC$;HyD3d_=5|c~r0Qb@7kDpfeDWv3P zrg9JV9fiqr%ZKe&A|nOgYwol5ObV115CQ09kGC z-kAdj<5iAp0J?%TU%!4eoT(+Z7UW%sCkyit?WBuJZhBY7WovBAZB^b^>`F^)5IlQw z3!SKxRDxI)RqdjirATaKWJg0ofSPWB?M9+%---6-=Kg_!WSjcnq;8YOm|tgnPOW!h zV;)s%{%SfrIT;xdasTdJ$TklG?_bcYX!RvGM+4Q_oP-g@^794Ck zkbfeo94#CT4Gr44-ZJm9moHzIlt{9&CT3;Lfb0Y_hSGOeaN^TTdNFSR8z2}4qLq~u zP2H)}r_nz}rlbs_MnM>Hn(S??tgL+h9=t+MJ9(+sKA~;Q0MuFy2{vUe#~wy|J!X~s^|@42{*|`YJSpETi|vO$ldeFB_Gn+s+K0LZsTKp$%!SH~H~n5?zD~UCH3?=5|R_lb(()^jbS77nktTgR>c3)6YKzxu7BP;Lh+(tzevf&JX&ef|-tgL!6 zGBWn|S$FOz^_6;RGuf9U=lS%Nd3z7m?ZQ&p2bG~1AytA0LebpA#N>hE133*ue(T5% zDk?EVva&KI^R8@gLyOY`Td0}#FIY-Tm(ksMkcvtIQVAOQ1SBZ1Mu1r<8=G*l$ka#- zhmRZy6#g)J$;iY64JAN^3lcc8yGg0#6tZYmJYHg?8v_u7cNKLUIXipu`=|<~p+XiH z8e_FHXN*3Wd5MLlW}ztHGdPv{!2{s3x~(owBN}erya}E46d`IrvJ}rJDa1fUfXRlX z_uhJ36Tm+-(@eTUGNma^ox#DusJYP6TJJa+MxJup%aHNvRWSryzzkIAj&Cj_4IL^` zwYGQ;7TeZI(%8{H zK4@Aq&=6V`Hho3K#ihkLc&_g;HaUzWgd~}wVL+mwuU-3!i=rIlgFk~bg& zXJsejQA2zt=Sy$PszVz_7|6D4+4Az)GqmlE(L$-`+a34u^~z@g);5gvyt|+edDdupEHSQfDV93y!Yb84|EHo_mQR@ zgEwy2;IqCHtJs;wJ4);C_}X!0-YSsg-l?6*IonihY;1f9X$oN{kLTp(LIz-%`0*nK zb_cYl!NJX`>Z~BZV{Xjt(!6}x)XeOZ_%(8$jf8@W5R9(u`&3a`xrq&AlEQHC+R0Iw z)AIdT$QYT67as=h`*^h-m`zV_wtWC>)+bqZ+@6213=1E$HrDQ=+@mc;{_GXC`qA<@OupeSQJy zXk?l_T0n$BkMa5N*;2&6`f*9;x{gm}MMWor;f(+;Geg51aKBPgQhWK5$%GCM!``#L z6XEs!+qcIR0H-0!=dWG62A0_I`gOB+RaNr9J)IPSjC3~`X;}mfd_l^^$BEh*U~7&V z+Sz4dhu^(R=^$f%Vn_ibqo1Fmnwpx1Mw>4cErk21ojC|9FoRXyV<00_Q&TISY+4;9 zJ8<9tDWTuJi-2U7_UeY~Cop*z6BFLerR|&GBmUkdjcIj9q(lrlqf_)Yz4s&cwwT2uG5t_2T z95dQkuHc*;VQ1$BG~I4)ZlSx5KnaP9k4LW!%R^o4zTVzm;luX)F0fBPj*X4gR#T&{ zH+dhyhqLih*|Nch6s#M;D*pNQ-|0pD4I5`IIi&w5(0efOS4&Lkm6v6!b<8k5y2K_MIF>xlx{fCj=l0pA+OBLiD=F|rB+%fOh zZTBiI?Q6GB`Gzj&Kx~6Qesq?8QB+W1^F=6$zuUZtlrCW;`^SIqosn|p-w*xu_f2IX zYUhe0G-X7H-jeU3_~+_>D)OpexGQqo&vi~Y=dKCq-Bs<$tS&aNlD3;9ecr*w3k}x z%NiMR)qJk2qr?^VncuzGnQOtJ7;qVyBaCIxeQ;=Ax65%!fOHXn8*!G8ddSgXBlOWV z?Ttp1t|^d#9NX_JAt^~(9n<4J4*~+n$;tQb+gDF&K7mxcZq|~g-+}WH5)$%0uTSITwYDl2+lU;_D4<^sd7~?{g&Nmy%A0kH zSGV}Gme!ZgpBDjRfF%Htjvqa0J`f}ibu&I*2R0}mu*^&_l>XRWT`&K7WzeSeEu`y2 zBFB+588-a$=f55ztv>l?gfag5=aIF@%Z2vv&(zcm=@xw(7`R0^fByWNj$U+h`+cDk zQw@W1*S}%2fqbH_AyHaSSy>s8kgdab%sd?XvdrtZaMwN*d&si@mX(E@{_Ae~o2SPy zY>??!lgEYXl3Vj!nt38LZnLE)9>@=PLPX5~jRa-)H2IOkhgEQi-o0~!Km+sKw5F8T z>gCIqqX1t*LQcMsvJL@J1}*09{reN0xi6Pmv7^-zB(!8@D;gTcY^(qeej!mwc`Qx= zVxj5-fab-ofmy>&Kz;CnF#$=l{z$R+`dVY`aneK6-xMxf2)}jf77Q!uEQcYo0mrbg zBp?wvNl-w2ESuZy4y%r-DL~fung{!=;;MkaZk98v_k;WL4F-z_#x*_t2sC-wXPOzh zI44+dDjC2{ym|1cDNgJbp)=n`&tHX>hDM+UYLmA`7%Dq}1xOZI)2aTq7VWRh#DB*e zvmnSqMS`vCoTlc1RQDu|!Y;$nD(M7U|J8dU+cb+2m@wNc$h~U+d-NFibxq@K}1w}|bb}|*!J&{Y$ z$K)uPr9299$*~*Yl34cH5i98~B75+}iRD7c$*jy7S-t&0oULKIw~U4{^=98`5@tQW24pA zirWy?FI~7GVcRd$MaFu*i0NAXYkektxCbW?_Ml)YH?GhC}QK-;pCuj*h~|k9V}UCxAtBuz3IO z-Pri}0oABSPo9wIS-eB`moZ=g)DuR=XoP-qIKV72E_h<_T?hISjumZsw@|MBE zHtkDouC$2*Pi6kr!a!>X$xlpesXVD42;oqE71Gz4Ybit?(q_Rp~~3HX>!g>l26 zlv7;HL_SeYwz9amok=wKV>=9)Bv(KQI~h6=iNl9qK7T&?#|r>YNgnrWWY65(>)6ml z;sE+J92Uu*B1wh@i5Sq>*yw$^A8;WiHWud%i58wQT=EwE8F)WAIXThes46b+*|p1z z&kzaJ+_xFA{{rw`MFsu^&i(s?6B5)Csz5rCES>UxieTlh|G4nx@$K8ypc?C)p|oR( z!;1XkRdl`Sy(P4-RFa?qD1Y!p|3dEPtA+m8xO}J-*bT) zw5#|8%EX!kKNOifLJiF{gEj{X}P4Csv`3`QMAN}W=VC3kX|G?5Q>?%cV4&8Iei zQ7+?Gf4{yjK`^4x*uvBlnZ9KX+Xs!6Rd_jsG{d9({6R1Rnt~B*h3n%))FNC?aAb_~ zQ!^g*U6VIA|3OM)3+U~An~}QeQ()T+4W;>Ah=s_f^VKVM5v;X-@%;Jt=geCkWhB@0 zBCffr&ty-L68FBndzTS=NSAaJPR`DgX!yn*d;43Vy}W45_8n(J_z@S zi1r&BH~CRBpU&tX;u6T-@#@hd$R+&7F*sc`%}r5)n~1B+zbxEn2fuxbk@l9Vb@&Ag zf6fO{j`JNK8!I(A9=lD|b$%=w&c+%z%L;NuNNY`y1yXIKhzJY&*_A&DlJ(cz95;OH zjw8*;PzSiUe9D=Nc1RdEL>ax0&?&U1AgU%l7om^3dKgLB>c|wxmnSJHYiaxGgtn5* z1il2bEhKv+kyH&Pw;R||zIyWHQ<1aH%x^ToFk>)?JB>k{hV>%)+#EI>)VP`HX*&l8 zQk!(iI-1PH1a=NY&@_>(qoV_y2P_u_1qH!*+n?XhKPd2}7y)5Vf=%V`-&+_Mkez(> zfny5974$c#(W6Oba{aid9KQAr4zb%_B_+Lm|9&NYSBST2KNQA*X~ea+sy_BOjsTGR zYvmSTL|ld}w1@di#MfXBP=kfs?-*~rdg+p%pmAPYnO?u2-)1}?F-0Q=kBPaqK6VwF zT3A29%vhvRY$RKsfNggpF)k{qZhIm=8+t?YHVU@E{{H5ea@qrG!U6)1!cY#j(edi* z{zhy2JAqFNte@=NOQHaXc=yS^vh~J2XvRS_NA2l{^2p&UV)N9W|Oa zn3Iks-hn$2`4Z)noy`bM*;7s^Nb;0OP+4EbfGlyXT2$e-K!;bq0J_Gv8MvrE1qvX# zV+xL>+~cQ660P9VjB9A`&KlzU?fp-a*Muxevy8uCl<`q4XVQ6uk-AFUrmDF;E zwo50rj4#q`qwHQ&O^{$DGNF79qxq$xqzph=#=gtTYk&K8BT>Slo!9;vHvM)sdA%o; z+qaK_Q^QuU5+Vke&4DsI3v>WZAD|Ax0tf+8zka)Bj?IT&|! zVUrvNnk5)W^_)^wm8TTctDm2pm7{Dqa0-$oAOW+u2nR-8igwE#C7xDVpl%vdN}lz7ngw}(RH3abEcXBv|B1LWkJE`sw#3~vFD1DeN0{K z<(vSWeCznEtfOf)VKRHuE-EM~%}!54JnjMc07N4s)YjWu`s~@=Mq|uy;Iy6Zrk?hW zE5|XxxxE*#0plb$Z&o0inu^&j&mql^N_j3v#l%3DHN>$XLV<<=sQzMaA%r%g+MT%m zA}x*2Q-qd|?kTp_qelXkp9K(AG=+^#P2fY2(QH6pp%mYxVv+y^)Ygu*yoWw{N3Y+V zJNeApdI}v>`lZaoGVY zwX%&LzzE+MbBxn_%a%J3wZr(5zF&j?Cnn|!$Ytu z*}&Q@hp)Z8Nr9MU{^qSRv7q}+BPp6Z9$#h&uBXDn;)CIMZviAK8ix?dk9~>mVEre#V`|c-5 zaKpnkm7k{fhCPk`&^wa#o;d8&_O_$z_dw=sxniVFo2p4rN7P5ZPphk|Xw92_(I2iG zn3!}JXk5DRs>9{MscrOPtL!>u8l$)UnQAOlY4-g5WH1mNz(9GU+}j{T+@R|2`s%PW z(-x19rq^E<&bu#AtZKLB&3`H4FE>yB{jB8&)4tWj^;Hx2&Cl+?Rqjo|$26Acd@opX z4$I4JI#de*rMALLk#{CDIkI8(dgoy?dqYu?q%)Y4=>wG6S|RSWz5Fe1V)dncK*OP&8E1A~$^a`$?r zv7gyoU_V7y_S@c3x_UK^sX>j-)tZC-%)BPJ`wvo--CsipCvX4w5l#EkEtgbZb>KqB%k?}sQdGs@Z5CxNZ1wTr9yGxdmSjNF zuV24LT5PKnzo2&Qt3fUG-uQWOH*5TqWWUwX8QuSnafgF~Vzg+N#Z${IMT&BA!nJmX zozC@ps^pZl1^qon7RmJ?pTH{BXdj!Fst$V+{j}+IXU>;d zy>QYC&%{bX<1}aw&n;SE*8qz^MY(}hsS zPxmqn_^ul+eCk>gGKq=bQ4$ra85KV(a=3`p|F1jx@PMI3Xf-I+rU_yNp2gktd-`FH z@lwhw>oo+i{$%M5I{J`YVd3k_Qhlq1nZ#PFU!T2JK9o4$uEy+L9sfZx5{(emz?MfZ|2%DnJo^{(MwUUDR|yV18Wxw#?jI zg@bQ&0A_z-V@jeqNYs=kiFx@KxdEbsM0b#Q5PT=`9siLI`L~z+<7rotGg=kKTe^R55&_y6^t|NQ&^=R5!3xBTCq{D0p0 z@0a{JkH7!^uY>t72mAL)|NWXjkNy98w5!_NUZteW4O8-R2<;~AMk+Wu2K9#q2Un@c z^a`CK$0pIplCBza0w8Pftv@XlZ-%$la!f37t(NrwNHsBb%^9R z5IrWC;yeij8ha{Ge##0fr8%YwXCsTQ|J+>7PuWd1JwN|c={oq^GON)!BBnD_Kg3(8 zI5&57?fW1~+5LsYph>>QOCjjR%k`i6V4c%{`!gu4XiS`t1|{xjo<9Aqv2i^AzM6Mg z`A*iFjH};2XXtsq5eFnL^X6bB-|R)Fsi_$_U}<1L0~_i&aw(_Pl!SzhwMQK9GKiy| z?GzIe1F{8A)OcoodU}ceeP%m(;2Zk!OshLSZw&)?M1+S!P#7l!6@b3{EoS$P-1VaW zCNv1pZg1SU;eFZoX?YN9EsNBcw0qt|UthynKI}N(vnZCUdOi)7EDn!SQeX!g7#f1M zSE85Vh|Uvoz*QX`H1`0NSGs6m*f?m9w>N^54}u4n>DZ2R?VKwrD(|bRsvewlaak<6 zM7eXP$x~rL!4@=&=g%(>?SwdgMO*vM?b~mQ821ZI9lu|N>w&X9bEa`}x{w+26(^U~ z<;x7a^FTC9-F5)L&d<+BV*#>lNYktLVrUS4!teR{&KEB1-kps%Q9quPyymUka)xT# zwgKf0-w*x2&RqYcIsPv�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 literal 0 HcmV?d00001 diff --git a/tests/typ/meta/figure.typ b/tests/typ/meta/figure.typ new file mode 100644 index 000000000..567e04310 --- /dev/null +++ b/tests/typ/meta/figure.typ @@ -0,0 +1,24 @@ +// Test figures. + +--- +#set page(width: 150pt) +#set figure(numbering: "I") + +We can clearly see that @fig-cylinder and +@tab-complex are relevant in this context. + +#figure( + table(columns: 2)[a][b], + caption: [The basic table.], +) + +#figure( + pad(y: -11pt, image("/cylinder.svg", height: 3cm)), + caption: [The basic shapes.], + numbering: "I", +) + +#figure( + table(columns: 3)[a][b][c][d][e][f], + caption: [The complex table.], +)