diff options
Diffstat (limited to 'library/src/meta')
| -rw-r--r-- | library/src/meta/bibliography.rs | 29 | ||||
| -rw-r--r-- | library/src/meta/footnote.rs | 217 | ||||
| -rw-r--r-- | library/src/meta/mod.rs | 3 |
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); |
