mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Use Outer(H|V)Alignment
to constraint types (#3465)
This commit is contained in:
parent
d0dd81cddf
commit
f54d68daff
@ -5,7 +5,8 @@ use ecow::{eco_format, EcoString};
|
|||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, func, scope, ty, Content, Fold, Packed, Repr, Resolve, Show, StyleChain,
|
cast, elem, func, scope, ty, CastInfo, Content, Fold, FromValue, IntoValue, Packed,
|
||||||
|
Reflect, Repr, Resolve, Show, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::layout::{Abs, Axes, Axis, Dir, Side};
|
use crate::layout::{Abs, Axes, Axis, Dir, Side};
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
@ -108,7 +109,7 @@ impl Alignment {
|
|||||||
/// The horizontal component.
|
/// The horizontal component.
|
||||||
pub const fn x(self) -> Option<HAlignment> {
|
pub const fn x(self) -> Option<HAlignment> {
|
||||||
match self {
|
match self {
|
||||||
Self::H(x) | Self::Both(x, _) => Some(x),
|
Self::H(h) | Self::Both(h, _) => Some(h),
|
||||||
Self::V(_) => None,
|
Self::V(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +117,7 @@ impl Alignment {
|
|||||||
/// The vertical component.
|
/// The vertical component.
|
||||||
pub const fn y(self) -> Option<VAlignment> {
|
pub const fn y(self) -> Option<VAlignment> {
|
||||||
match self {
|
match self {
|
||||||
Self::V(y) | Self::Both(_, y) => Some(y),
|
Self::V(v) | Self::Both(_, v) => Some(v),
|
||||||
Self::H(_) => None,
|
Self::H(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +189,7 @@ impl Add for Alignment {
|
|||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Self::H(x), Self::V(y)) | (Self::V(y), Self::H(x)) => Ok(x + y),
|
(Self::H(h), Self::V(v)) | (Self::V(v), Self::H(h)) => Ok(h + v),
|
||||||
(Self::H(_), Self::H(_)) => bail!("cannot add two horizontal alignments"),
|
(Self::H(_), Self::H(_)) => bail!("cannot add two horizontal alignments"),
|
||||||
(Self::V(_), Self::V(_)) => bail!("cannot add two vertical alignments"),
|
(Self::V(_), Self::V(_)) => bail!("cannot add two vertical alignments"),
|
||||||
(Self::H(_), Self::Both(..)) | (Self::Both(..), Self::H(_)) => {
|
(Self::H(_), Self::Both(..)) | (Self::Both(..), Self::H(_)) => {
|
||||||
@ -207,9 +208,9 @@ impl Add for Alignment {
|
|||||||
impl Repr for Alignment {
|
impl Repr for Alignment {
|
||||||
fn repr(&self) -> EcoString {
|
fn repr(&self) -> EcoString {
|
||||||
match self {
|
match self {
|
||||||
Self::H(x) => x.repr(),
|
Self::H(h) => h.repr(),
|
||||||
Self::V(y) => y.repr(),
|
Self::V(v) => v.repr(),
|
||||||
Self::Both(x, y) => eco_format!("{} + {}", x.repr(), y.repr()),
|
Self::Both(h, v) => eco_format!("{} + {}", h.repr(), v.repr()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +218,8 @@ impl Repr for Alignment {
|
|||||||
impl Fold for Alignment {
|
impl Fold for Alignment {
|
||||||
fn fold(self, outer: Self) -> Self {
|
fn fold(self, outer: Self) -> Self {
|
||||||
match (self, outer) {
|
match (self, outer) {
|
||||||
(Self::H(x), Self::V(y) | Self::Both(_, y)) => Self::Both(x, y),
|
(Self::H(h), Self::V(v) | Self::Both(_, v)) => Self::Both(h, v),
|
||||||
(Self::V(y), Self::H(x) | Self::Both(x, _)) => Self::Both(x, y),
|
(Self::V(v), Self::H(h) | Self::Both(h, _)) => Self::Both(h, v),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,6 +305,20 @@ impl From<HAlignment> for Alignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Alignment> for HAlignment {
|
||||||
|
type Error = EcoString;
|
||||||
|
|
||||||
|
fn try_from(value: Alignment) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Alignment::H(h) => Ok(h),
|
||||||
|
v => bail!(
|
||||||
|
"expected `start`, `left`, `center`, `right`, or `end`, found {}",
|
||||||
|
v.repr()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Resolve for HAlignment {
|
impl Resolve for HAlignment {
|
||||||
type Output = FixedAlignment;
|
type Output = FixedAlignment;
|
||||||
|
|
||||||
@ -315,10 +330,7 @@ impl Resolve for HAlignment {
|
|||||||
cast! {
|
cast! {
|
||||||
HAlignment,
|
HAlignment,
|
||||||
self => Alignment::H(self).into_value(),
|
self => Alignment::H(self).into_value(),
|
||||||
align: Alignment => match align {
|
align: Alignment => Self::try_from(align)?,
|
||||||
Alignment::H(v) => v,
|
|
||||||
v => bail!("expected `start`, `left`, `center`, `right`, or `end`, found {}", v.repr()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A horizontal alignment which only allows `left`/`right` and `start`/`end`,
|
/// A horizontal alignment which only allows `left`/`right` and `start`/`end`,
|
||||||
@ -332,6 +344,18 @@ pub enum OuterHAlignment {
|
|||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OuterHAlignment {
|
||||||
|
/// Resolve the axis alignment based on the horizontal direction.
|
||||||
|
pub const fn fix(self, dir: Dir) -> FixedAlignment {
|
||||||
|
match (self, dir.is_positive()) {
|
||||||
|
(Self::Start, true) | (Self::End, false) => FixedAlignment::Start,
|
||||||
|
(Self::Left, _) => FixedAlignment::Start,
|
||||||
|
(Self::Right, _) => FixedAlignment::End,
|
||||||
|
(Self::End, true) | (Self::Start, false) => FixedAlignment::End,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<OuterHAlignment> for HAlignment {
|
impl From<OuterHAlignment> for HAlignment {
|
||||||
fn from(value: OuterHAlignment) -> Self {
|
fn from(value: OuterHAlignment) -> Self {
|
||||||
match value {
|
match value {
|
||||||
@ -343,16 +367,24 @@ impl From<OuterHAlignment> for HAlignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Alignment> for OuterHAlignment {
|
||||||
|
type Error = EcoString;
|
||||||
|
|
||||||
|
fn try_from(value: Alignment) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Alignment::H(HAlignment::Start) => Ok(Self::Start),
|
||||||
|
Alignment::H(HAlignment::Left) => Ok(Self::Left),
|
||||||
|
Alignment::H(HAlignment::Right) => Ok(Self::Right),
|
||||||
|
Alignment::H(HAlignment::End) => Ok(Self::End),
|
||||||
|
v => bail!("expected `start`, `left`, `right`, or `end`, found {}", v.repr()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cast! {
|
cast! {
|
||||||
OuterHAlignment,
|
OuterHAlignment,
|
||||||
self => HAlignment::from(self).into_value(),
|
self => HAlignment::from(self).into_value(),
|
||||||
align: Alignment => match align {
|
align: Alignment => Self::try_from(align)?,
|
||||||
Alignment::H(HAlignment::Start) => Self::Start,
|
|
||||||
Alignment::H(HAlignment::Left) => Self::Left,
|
|
||||||
Alignment::H(HAlignment::Right) => Self::Right,
|
|
||||||
Alignment::H(HAlignment::End) => Self::End,
|
|
||||||
v => bail!("expected `start`, `left`, `right`, or `end`, found {}", v.repr()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where to align something vertically.
|
/// Where to align something vertically.
|
||||||
@ -408,13 +440,21 @@ impl From<VAlignment> for Alignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Alignment> for VAlignment {
|
||||||
|
type Error = EcoString;
|
||||||
|
|
||||||
|
fn try_from(value: Alignment) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Alignment::V(v) => Ok(v),
|
||||||
|
v => bail!("expected `top`, `horizon`, or `bottom`, found {}", v.repr()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cast! {
|
cast! {
|
||||||
VAlignment,
|
VAlignment,
|
||||||
self => Alignment::V(self).into_value(),
|
self => Alignment::V(self).into_value(),
|
||||||
align: Alignment => match align {
|
align: Alignment => Self::try_from(align)?,
|
||||||
Alignment::V(v) => v,
|
|
||||||
v => bail!("expected `top`, `horizon`, or `bottom`, found {}", v.repr()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A vertical alignment which only allows `top` and `bottom`, thus excluding
|
/// A vertical alignment which only allows `top` and `bottom`, thus excluding
|
||||||
@ -426,6 +466,16 @@ pub enum OuterVAlignment {
|
|||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OuterVAlignment {
|
||||||
|
/// Resolve the axis alignment based on the vertical direction.
|
||||||
|
pub const fn fix(self) -> FixedAlignment {
|
||||||
|
match self {
|
||||||
|
Self::Top => FixedAlignment::Start,
|
||||||
|
Self::Bottom => FixedAlignment::End,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<OuterVAlignment> for VAlignment {
|
impl From<OuterVAlignment> for VAlignment {
|
||||||
fn from(value: OuterVAlignment) -> Self {
|
fn from(value: OuterVAlignment) -> Self {
|
||||||
match value {
|
match value {
|
||||||
@ -435,13 +485,120 @@ impl From<OuterVAlignment> for VAlignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Alignment> for OuterVAlignment {
|
||||||
|
type Error = EcoString;
|
||||||
|
|
||||||
|
fn try_from(value: Alignment) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Alignment::V(VAlignment::Top) => Ok(Self::Top),
|
||||||
|
Alignment::V(VAlignment::Bottom) => Ok(Self::Bottom),
|
||||||
|
v => bail!("expected `top` or `bottom`, found {}", v.repr()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cast! {
|
cast! {
|
||||||
OuterVAlignment,
|
OuterVAlignment,
|
||||||
self => VAlignment::from(self).into_value(),
|
self => VAlignment::from(self).into_value(),
|
||||||
align: Alignment => match align {
|
align: Alignment => Self::try_from(align)?,
|
||||||
Alignment::V(VAlignment::Top) => Self::Top,
|
}
|
||||||
Alignment::V(VAlignment::Bottom) => Self::Bottom,
|
|
||||||
v => bail!("expected `top` or `bottom`, found {}", v.repr()),
|
/// An internal representation that combines horizontal or vertical alignments. The
|
||||||
|
/// allowed alignment positions are designated by the type parameter `H` and `V`.
|
||||||
|
///
|
||||||
|
/// This is not user-visible, but an internal type to impose type safety. For example,
|
||||||
|
/// `SpecificAlignment<HAlignment, OuterVAlignment>` does not allow vertical alignment
|
||||||
|
/// position "center", because `V = OuterVAlignment` doesn't have it.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum SpecificAlignment<H, V> {
|
||||||
|
H(H),
|
||||||
|
V(V),
|
||||||
|
Both(H, V),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, V> SpecificAlignment<H, V>
|
||||||
|
where
|
||||||
|
H: Copy,
|
||||||
|
V: Copy,
|
||||||
|
{
|
||||||
|
/// The horizontal component.
|
||||||
|
pub const fn x(self) -> Option<H> {
|
||||||
|
match self {
|
||||||
|
Self::H(h) | Self::Both(h, _) => Some(h),
|
||||||
|
Self::V(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The vertical component.
|
||||||
|
pub const fn y(self) -> Option<V> {
|
||||||
|
match self {
|
||||||
|
Self::V(v) | Self::Both(_, v) => Some(v),
|
||||||
|
Self::H(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, V> From<SpecificAlignment<H, V>> for Alignment
|
||||||
|
where
|
||||||
|
HAlignment: From<H>,
|
||||||
|
VAlignment: From<V>,
|
||||||
|
{
|
||||||
|
fn from(value: SpecificAlignment<H, V>) -> Self {
|
||||||
|
type FromType<H, V> = SpecificAlignment<H, V>;
|
||||||
|
match value {
|
||||||
|
FromType::H(h) => Self::H(HAlignment::from(h)),
|
||||||
|
FromType::V(v) => Self::V(VAlignment::from(v)),
|
||||||
|
FromType::Both(h, v) => Self::Both(HAlignment::from(h), VAlignment::from(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, V> Reflect for SpecificAlignment<H, V>
|
||||||
|
where
|
||||||
|
H: Reflect,
|
||||||
|
V: Reflect,
|
||||||
|
{
|
||||||
|
fn input() -> CastInfo {
|
||||||
|
H::input() + V::input()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output() -> CastInfo {
|
||||||
|
H::output() + V::output()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn castable(value: &Value) -> bool {
|
||||||
|
H::castable(value) || V::castable(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, V> IntoValue for SpecificAlignment<H, V>
|
||||||
|
where
|
||||||
|
HAlignment: From<H>,
|
||||||
|
VAlignment: From<V>,
|
||||||
|
{
|
||||||
|
fn into_value(self) -> Value {
|
||||||
|
Alignment::from(self).into_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, V> FromValue for SpecificAlignment<H, V>
|
||||||
|
where
|
||||||
|
H: Reflect + TryFrom<Alignment, Error = EcoString>,
|
||||||
|
V: Reflect + TryFrom<Alignment, Error = EcoString>,
|
||||||
|
{
|
||||||
|
fn from_value(value: Value) -> StrResult<Self> {
|
||||||
|
if Alignment::castable(&value) {
|
||||||
|
let align = Alignment::from_value(value)?;
|
||||||
|
let result = match align {
|
||||||
|
Alignment::H(_) => Self::H(H::try_from(align)?),
|
||||||
|
Alignment::V(_) => Self::V(V::try_from(align)?),
|
||||||
|
Alignment::Both(h, v) => {
|
||||||
|
Self::Both(H::try_from(h.into())?, V::try_from(v.into())?)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
Err(Self::error(&value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ use crate::foundations::{
|
|||||||
use crate::introspection::{Counter, CounterKey, ManualPageCounter};
|
use crate::introspection::{Counter, CounterKey, ManualPageCounter};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Alignment, Axes, ColumnsElem, Dir, Frame, HAlignment, LayoutMultiple,
|
Abs, AlignElem, Alignment, Axes, ColumnsElem, Dir, Frame, HAlignment, LayoutMultiple,
|
||||||
Length, Point, Ratio, Regions, Rel, Sides, Size, VAlignment,
|
Length, OuterVAlignment, Point, Ratio, Regions, Rel, Sides, Size, SpecificAlignment,
|
||||||
|
VAlignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::model::Numbering;
|
use crate::model::Numbering;
|
||||||
use crate::syntax::Spanned;
|
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
use crate::util::{NonZeroExt, Numeric, Scalar};
|
use crate::util::{NonZeroExt, Numeric, Scalar};
|
||||||
use crate::visualize::Paint;
|
use crate::visualize::Paint;
|
||||||
@ -221,17 +221,8 @@ pub struct PageElem {
|
|||||||
///
|
///
|
||||||
/// #lorem(30)
|
/// #lorem(30)
|
||||||
/// ```
|
/// ```
|
||||||
#[default(HAlignment::Center + VAlignment::Bottom)]
|
#[default(SpecificAlignment::Both(HAlignment::Center, OuterVAlignment::Bottom))]
|
||||||
#[parse({
|
pub number_align: SpecificAlignment<HAlignment, OuterVAlignment>,
|
||||||
let option: Option<Spanned<Alignment>> = args.named("number-align")?;
|
|
||||||
if let Some(Spanned { v: align, span }) = option {
|
|
||||||
if align.y() == Some(VAlignment::Horizon) {
|
|
||||||
bail!(span, "page number cannot be `horizon`-aligned");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
option.map(|spanned| spanned.v)
|
|
||||||
})]
|
|
||||||
pub number_align: Alignment,
|
|
||||||
|
|
||||||
/// The page's header. Fills the top margin of each page.
|
/// The page's header. Fills the top margin of each page.
|
||||||
///
|
///
|
||||||
@ -440,7 +431,7 @@ impl Packed<PageElem> {
|
|||||||
counter
|
counter
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if matches!(number_align.y(), Some(VAlignment::Top)) {
|
if matches!(number_align.y(), Some(OuterVAlignment::Top)) {
|
||||||
header = if header.is_some() { header } else { numbering_marginal };
|
header = if header.is_some() { header } else { numbering_marginal };
|
||||||
} else {
|
} else {
|
||||||
footer = if footer.is_some() { footer } else { numbering_marginal };
|
footer = if footer.is_some() { footer } else { numbering_marginal };
|
||||||
|
@ -14,10 +14,10 @@ use crate::introspection::{
|
|||||||
Count, Counter, CounterKey, CounterUpdate, Locatable, Location,
|
Count, Counter, CounterKey, CounterUpdate, Locatable, Location,
|
||||||
};
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Alignment, BlockElem, Em, HAlignment, Length, PlaceElem, VAlignment, VElem,
|
Alignment, BlockElem, Em, HAlignment, Length, OuterVAlignment, PlaceElem, VAlignment,
|
||||||
|
VElem,
|
||||||
};
|
};
|
||||||
use crate::model::{Numbering, NumberingPattern, Outlinable, Refable, Supplement};
|
use crate::model::{Numbering, NumberingPattern, Outlinable, Refable, Supplement};
|
||||||
use crate::syntax::Spanned;
|
|
||||||
use crate::text::{Lang, Region, TextElem};
|
use crate::text::{Lang, Region, TextElem};
|
||||||
use crate::util::NonZeroExt;
|
use crate::util::NonZeroExt;
|
||||||
use crate::visualize::ImageElem;
|
use crate::visualize::ImageElem;
|
||||||
@ -310,10 +310,9 @@ impl Show for Packed<FigureElem> {
|
|||||||
// Build the caption, if any.
|
// Build the caption, if any.
|
||||||
if let Some(caption) = self.caption(styles) {
|
if let Some(caption) = self.caption(styles) {
|
||||||
let v = VElem::weak(self.gap(styles).into()).pack();
|
let v = VElem::weak(self.gap(styles).into()).pack();
|
||||||
realized = if caption.position(styles) == VAlignment::Bottom {
|
realized = match caption.position(styles) {
|
||||||
realized + v + caption.pack()
|
OuterVAlignment::Top => caption.pack() + v + realized,
|
||||||
} else {
|
OuterVAlignment::Bottom => realized + v + caption.pack(),
|
||||||
caption.pack() + v + realized
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,17 +457,8 @@ pub struct FigureCaption {
|
|||||||
/// )
|
/// )
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[default(VAlignment::Bottom)]
|
#[default(OuterVAlignment::Bottom)]
|
||||||
#[parse({
|
pub position: OuterVAlignment,
|
||||||
let option: Option<Spanned<VAlignment>> = args.named("position")?;
|
|
||||||
if let Some(Spanned { v: align, span }) = option {
|
|
||||||
if align == VAlignment::Horizon {
|
|
||||||
bail!(span, "expected `top` or `bottom`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
option.map(|spanned| spanned.v)
|
|
||||||
})]
|
|
||||||
pub position: VAlignment,
|
|
||||||
|
|
||||||
/// The separator which will appear between the number and body.
|
/// The separator which will appear between the number and body.
|
||||||
///
|
///
|
||||||
|
@ -21,5 +21,5 @@
|
|||||||
#block(width: 100%, height: 100%, fill: aqua.lighten(50%))
|
#block(width: 100%, height: 100%, fill: aqua.lighten(50%))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 25-39 page number cannot be `horizon`-aligned
|
// Error: 25-39 expected `top` or `bottom`, found horizon
|
||||||
#set page(number-align: left + horizon)
|
#set page(number-align: left + horizon)
|
||||||
|
@ -54,3 +54,11 @@
|
|||||||
caption: [Hi],
|
caption: [Hi],
|
||||||
supplement: [B],
|
supplement: [B],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Ref: false
|
||||||
|
#set figure.caption(position: top)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 31-38 expected `top` or `bottom`, found horizon
|
||||||
|
#set figure.caption(position: horizon)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user