summaryrefslogtreecommitdiff
path: root/library/src/meta
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/meta')
-rw-r--r--library/src/meta/bibliography.rs29
-rw-r--r--library/src/meta/footnote.rs217
-rw-r--r--library/src/meta/mod.rs3
3 files changed, 238 insertions, 11 deletions
diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs
index cdb6a5d6..2bf0e6f4 100644
--- a/library/src/meta/bibliography.rs
+++ b/library/src/meta/bibliography.rs
@@ -11,7 +11,7 @@ use typst::util::option_eq;
use super::{LinkElem, LocalName, RefElem};
use crate::layout::{BlockElem, GridElem, ParElem, Sizing, TrackSizings, VElem};
-use crate::meta::HeadingElem;
+use crate::meta::{FootnoteElem, HeadingElem};
use crate::prelude::*;
use crate::text::TextElem;
@@ -243,6 +243,9 @@ pub enum BibliographyStyle {
/// The Chicago Author Date style. Based on the 17th edition of the Chicago
/// Manual of Style, Chapter 15.
ChicagoAuthorDate,
+ /// The Chicago Notes style. Based on the 17th edition of the Chicago
+ /// Manual of Style, Chapter 14.
+ ChicagoNotes,
/// The style of the Institute of Electrical and Electronics Engineers.
/// Based on the 2018 IEEE Reference Guide.
Ieee,
@@ -257,6 +260,7 @@ impl BibliographyStyle {
match self {
Self::Apa => CitationStyle::ChicagoAuthorDate,
Self::ChicagoAuthorDate => CitationStyle::ChicagoAuthorDate,
+ Self::ChicagoNotes => CitationStyle::ChicagoNotes,
Self::Ieee => CitationStyle::Numerical,
Self::Mla => CitationStyle::ChicagoAuthorDate,
}
@@ -385,7 +389,10 @@ pub enum CitationStyle {
/// The Chicago Author Date style. Based on the 17th edition of the Chicago
/// Manual of Style, Chapter 15.
ChicagoAuthorDate,
- /// The Chicago-like author-title format. Results could look like this:
+ /// The Chicago Notes style. Based on the 17th edition of the Chicago
+ /// Manual of Style, Chapter 14.
+ ChicagoNotes,
+ /// A Chicago-like author-title format. Results could look like this:
/// Prokopov, “It Is Fast or It Is Wrong”.
ChicagoAuthorTitle,
}
@@ -487,6 +494,7 @@ fn create(
CitationStyle::ChicagoAuthorDate => {
Box::new(style::ChicagoAuthorDate::new())
}
+ CitationStyle::ChicagoNotes => Box::new(style::ChicagoNotes::new()),
CitationStyle::ChicagoAuthorTitle => {
Box::new(style::AuthorTitle::new())
}
@@ -537,6 +545,10 @@ fn create(
};
}
+ if style == CitationStyle::ChicagoNotes {
+ content = FootnoteElem::new(content).pack();
+ }
+
(location, Some(content))
})
.collect();
@@ -544,6 +556,7 @@ fn create(
let bibliography_style: Box<dyn style::BibliographyStyle> = match style {
BibliographyStyle::Apa => Box::new(style::Apa::new()),
BibliographyStyle::ChicagoAuthorDate => Box::new(style::ChicagoAuthorDate::new()),
+ BibliographyStyle::ChicagoNotes => Box::new(style::ChicagoNotes::new()),
BibliographyStyle::Ieee => Box::new(style::Ieee::new()),
BibliographyStyle::Mla => Box::new(style::Mla::new()),
};
@@ -552,24 +565,18 @@ fn create(
.bibliography(&*bibliography_style, None)
.into_iter()
.map(|reference| {
- // Make link from citation to here work.
- let backlink = {
- let mut content = Content::empty();
- content.set_location(ref_location(reference.entry));
- MetaElem::set_data(vec![Meta::Elem(content)])
- };
-
+ let backlink = ref_location(reference.entry);
let prefix = reference.prefix.map(|prefix| {
// Format and link to first citation.
let bracketed = prefix.with_default_brackets(&*citation_style);
format_display_string(&bracketed, None, span)
.linked(Destination::Location(ids[reference.entry.key()]))
- .styled(backlink.clone())
+ .backlinked(backlink)
});
let mut reference = format_display_string(&reference.display, None, span);
if prefix.is_none() {
- reference = reference.styled(backlink);
+ reference = reference.backlinked(backlink);
}
(prefix, reference)
diff --git a/library/src/meta/footnote.rs b/library/src/meta/footnote.rs
new file mode 100644
index 00000000..22de91c3
--- /dev/null
+++ b/library/src/meta/footnote.rs
@@ -0,0 +1,217 @@
+use std::str::FromStr;
+
+use super::{Counter, Numbering, NumberingPattern};
+use crate::layout::{HElem, ParElem};
+use crate::prelude::*;
+use crate::text::{SuperElem, TextElem, TextSize};
+use crate::visualize::LineElem;
+
+/// A footnote.
+///
+/// Include additional remarks and references on the same page with footnotes. A
+/// footnote will insert a superscript number that links to the note at the
+/// bottom of the page. Notes are numbered sequentially throughout your document
+/// and can break across multiple pages.
+///
+/// To customize the appearance of the entry in the footnote listing, see
+/// [`footnote.entry`]($func/footnote.entry). The footnote itself is realized as
+/// a normal superscript, so you can use a set rule on the
+/// [`super`]($func/super) function to customize it.
+///
+/// ## Example { #example }
+/// ```example
+/// Check the docs for more details.
+/// #footnote[https://typst.app/docs]
+/// ```
+///
+/// The footnote automatically attaches itself to the preceding word, even if
+/// there is a space before it in the markup. To force space, you can use the
+/// string `[#" "]` or explicit [horizontal spacing]($func/h).
+///
+/// Display: Footnote
+/// Category: meta
+#[element(Locatable, Synthesize, Show)]
+#[scope(
+ scope.define("entry", FootnoteEntry::func());
+ scope
+)]
+pub struct FootnoteElem {
+ /// How to number footnotes.
+ ///
+ /// By default, the footnote numbering continues throughout your document.
+ /// If you prefer per-page footnote numbering, you can reset the footnote
+ /// [counter]($func/counter) in the page [header]($func/page.header). In the
+ /// future, there might be a simpler way to achieve this.
+ ///
+ /// ```example
+ /// #set footnote(numbering: "*")
+ ///
+ /// Footnotes:
+ /// #footnote[Star],
+ /// #footnote[Dagger]
+ /// ```
+ #[default(Numbering::Pattern(NumberingPattern::from_str("1").unwrap()))]
+ pub numbering: Numbering,
+
+ /// The content to put into the footnote.
+ #[required]
+ pub body: Content,
+}
+
+impl Synthesize for FootnoteElem {
+ fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
+ self.push_numbering(self.numbering(styles));
+ Ok(())
+ }
+}
+
+impl Show for FootnoteElem {
+ #[tracing::instrument(name = "FootnoteElem::show", skip_all)]
+ fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
+ let loc = self.0.location().unwrap();
+ let numbering = self.numbering(styles);
+ let counter = Counter::of(Self::func());
+ let num = counter.at(vt, loc)?.display(vt, &numbering)?;
+ let sup = SuperElem::new(num).pack();
+ let hole = HElem::new(Abs::zero().into()).with_weak(true).pack();
+ let loc = self.0.location().unwrap().variant(1);
+ Ok(hole + sup.linked(Destination::Location(loc)))
+ }
+}
+
+/// An entry in a footnote list.
+///
+/// This function is not intended to be called directly. Instead, it is used
+/// in set and show rules to customize footnote listings.
+///
+/// ## Example { #example }
+/// ```example
+/// #show footnote.entry: set text(red)
+///
+/// My footnote listing
+/// #footnote[It's down here]
+/// has red text!
+/// ```
+///
+/// Display: Footnote Entry
+/// Category: meta
+#[element(Show, Finalize)]
+pub struct FootnoteEntry {
+ /// The footnote for this entry. It's location can be used to determine
+ /// the footnote counter state.
+ ///
+ /// ```example
+ /// #show footnote.entry: it => {
+ /// let loc = it.note.location()
+ /// numbering(
+ /// "1: ",
+ /// ..counter(footnote).at(loc),
+ /// )
+ /// it.note.body
+ /// }
+ ///
+ /// Customized #footnote[Hello]
+ /// listing #footnote[World! 🌏]
+ /// ```
+ #[required]
+ pub note: FootnoteElem,
+
+ /// The separator between the document body and the footnote listing.
+ ///
+ /// ```example
+ /// #set footnote.entry(
+ /// separator: repeat[.]
+ /// )
+ ///
+ /// Testing a different separator.
+ /// #footnote[
+ /// Unconventional, but maybe
+ /// not that bad?
+ /// ]
+ /// ```
+ #[default(
+ LineElem::new()
+ .with_length(Ratio::new(0.3).into())
+ .with_stroke(PartialStroke {
+ thickness: Smart::Custom(Abs::pt(0.5).into()),
+ ..Default::default()
+ })
+ .pack()
+ )]
+ pub separator: Content,
+
+ /// The amount of clearance between the document body and the separator.
+ ///
+ /// ```example
+ /// #set footnote.entry(clearance: 3em)
+ ///
+ /// Footnotes also need ...
+ /// #footnote[
+ /// ... some space to breathe.
+ /// ]
+ /// ```
+ #[default(Em::new(1.0).into())]
+ #[resolve]
+ pub clearance: Length,
+
+ /// The gap between footnote entries.
+ ///
+ /// ```example
+ /// #set footnote.entry(gap: 0.8em)
+ ///
+ /// Footnotes:
+ /// #footnote[Spaced],
+ /// #footnote[Apart]
+ /// ```
+ #[default(Em::new(0.5).into())]
+ #[resolve]
+ pub gap: Length,
+
+ /// The indent of each footnote entry.
+ ///
+ /// ```example
+ /// #set footnote.entry(indent: 0em)
+ ///
+ /// Footnotes:
+ /// #footnote[No],
+ /// #footnote[Indent]
+ /// ```
+ #[default(Em::new(1.0).into())]
+ pub indent: Length,
+}
+
+impl Show for FootnoteEntry {
+ fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
+ let note = self.note();
+ let number_gap = Em::new(0.05);
+ let numbering = note.numbering(StyleChain::default());
+ let counter = Counter::of(FootnoteElem::func());
+ let loc = note.0.location().unwrap();
+ let num = counter.at(vt, loc)?.display(vt, &numbering)?;
+ let sup = SuperElem::new(num)
+ .pack()
+ .linked(Destination::Location(loc))
+ .backlinked(loc.variant(1));
+ Ok(Content::sequence([
+ HElem::new(self.indent(styles).into()).pack(),
+ sup,
+ HElem::new(number_gap.into()).with_weak(true).pack(),
+ note.body(),
+ ]))
+ }
+}
+
+impl Finalize for FootnoteEntry {
+ fn finalize(&self, realized: Content, _: StyleChain) -> Content {
+ let text_size = Em::new(0.85);
+ let leading = Em::new(0.5);
+ realized
+ .styled(ParElem::set_leading(leading.into()))
+ .styled(TextElem::set_size(TextSize(text_size.into())))
+ }
+}
+
+cast_from_value! {
+ FootnoteElem,
+ v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())),
+}
diff --git a/library/src/meta/mod.rs b/library/src/meta/mod.rs
index 61028bf5..0cbbafff 100644
--- a/library/src/meta/mod.rs
+++ b/library/src/meta/mod.rs
@@ -5,6 +5,7 @@ mod context;
mod counter;
mod document;
mod figure;
+mod footnote;
mod heading;
mod link;
mod numbering;
@@ -18,6 +19,7 @@ pub use self::context::*;
pub use self::counter::*;
pub use self::document::*;
pub use self::figure::*;
+pub use self::footnote::*;
pub use self::heading::*;
pub use self::link::*;
pub use self::numbering::*;
@@ -36,6 +38,7 @@ pub(super) fn define(global: &mut Scope) {
global.define("outline", OutlineElem::func());
global.define("heading", HeadingElem::func());
global.define("figure", FigureElem::func());
+ global.define("footnote", FootnoteElem::func());
global.define("cite", CiteElem::func());
global.define("bibliography", BibliographyElem::func());
global.define("locate", locate);