From 70a909b8badec1f14b67338a0e010e9a374866b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20d=27Herbais=20de=20Thun?= Date: Wed, 5 Apr 2023 15:04:31 +0200 Subject: [PATCH] Fixed page numbering (#594) --- library/src/layout/page.rs | 6 ++++++ library/src/meta/context.rs | 9 +++++++++ library/src/meta/outline.rs | 22 +++++++++++++++++----- src/doc.rs | 2 ++ src/eval/methods.rs | 3 ++- src/export/pdf/page.rs | 1 + src/export/render.rs | 1 + src/model/introspect.rs | 19 +++++++++++++++++-- 8 files changed, 55 insertions(+), 8 deletions(-) diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 8ad85c79b..0c70282df 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -324,8 +324,14 @@ impl PageElem { }); let footer_descent = self.footer_descent(styles); + let numbering_meta = FrameItem::Meta( + Meta::PageNumbering(self.numbering(styles).into()), + Size::zero(), + ); + // Realize overlays. for frame in &mut fragment { + frame.prepend(Point::zero(), numbering_meta.clone()); let size = frame.size(); let pad = padding.resolve(styles).relative_to(size); let pw = size.x - pad.left - pad.right; diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs index e36e6b284..7426d27d7 100644 --- a/library/src/meta/context.rs +++ b/library/src/meta/context.rs @@ -35,6 +35,15 @@ use crate::prelude::*; /// /// - returns: dictionary /// +/// ### page-numbering() +/// Returns the page numbering pattern of the page at this location. This can be +/// used when displaying the page counter in order to obtain the local numbering. +/// This is useful if you are building custom indices or outlines. +/// +/// If the page numbering is set to `none` at that location, this function returns `none`. +/// +/// - returns: string or function or none +/// /// Display: Locate /// Category: meta /// Returns: content diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs index b591dcee2..d25a1d1cc 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -1,4 +1,8 @@ -use super::{Counter, CounterKey, HeadingElem, LocalName, Refable}; +use std::str::FromStr; + +use super::{ + Counter, CounterKey, HeadingElem, LocalName, Numbering, NumberingPattern, Refable, +}; use crate::layout::{BoxElem, HElem, HideElem, ParbreakElem, RepeatElem}; use crate::prelude::*; use crate::text::{LinebreakElem, SpaceElem, TextElem}; @@ -205,6 +209,15 @@ impl Show for OutlineElem { // Add the outline of the element. seq.push(outline.linked(Destination::Location(location))); + let page_numbering = vt + .introspector + .page_numbering(location) + .cast::>() + .unwrap() + .unwrap_or_else(|| { + Numbering::Pattern(NumberingPattern::from_str("1").unwrap()) + }); + // Add filler symbols between the section name and page number. if let Some(filler) = self.fill(styles) { seq.push(SpaceElem::new().pack()); @@ -221,11 +234,10 @@ impl Show for OutlineElem { // Add the page number and linebreak. let page = Counter::new(CounterKey::Page) - // query the page counter state at location of heading .at(vt, location)? - .first(); - let end = TextElem::packed(eco_format!("{page}")); - seq.push(end.linked(Destination::Location(location))); + .display(vt, &page_numbering)?; + + seq.push(page.linked(Destination::Location(location))); seq.push(LinebreakElem::new().pack()); ancestors.push(elem); diff --git a/src/doc.rs b/src/doc.rs index 0f3ade022..aa460bd37 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -609,6 +609,8 @@ pub enum Meta { /// An identifiable element that produces something within the area this /// metadata is attached to. Elem(Content), + /// The numbering of the current page. + PageNumbering(Value), /// Indicates that content should be hidden. This variant doesn't appear /// in the final frames as it is removed alongside the content that should /// be hidden. diff --git a/src/eval/methods.rs b/src/eval/methods.rs index bfe9b0e47..b88bca509 100644 --- a/src/eval/methods.rs +++ b/src/eval/methods.rs @@ -154,6 +154,7 @@ pub fn call( match method { "page" => vm.vt.introspector.page(location).into(), "position" => vm.vt.introspector.position(location).into(), + "page-numbering" => vm.vt.introspector.page_numbering(location), _ => return missing(), } } else { @@ -308,7 +309,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] { ], "function" => &[("where", true), ("with", true)], "arguments" => &[("named", false), ("pos", false)], - "location" => &[("page", false), ("position", false)], + "location" => &[("page", false), ("position", false), ("page-numbering", false)], "counter" => &[ ("display", true), ("at", true), diff --git a/src/export/pdf/page.rs b/src/export/pdf/page.rs index 8f767e872..636d42c77 100644 --- a/src/export/pdf/page.rs +++ b/src/export/pdf/page.rs @@ -298,6 +298,7 @@ fn write_frame(ctx: &mut PageContext, frame: &Frame) { Meta::Link(dest) => write_link(ctx, pos, dest, *size), Meta::Elem(_) => {} Meta::Hide => {} + Meta::PageNumbering(_) => {} }, } } diff --git a/src/export/render.rs b/src/export/render.rs index 89e45b15e..18d8a74b1 100644 --- a/src/export/render.rs +++ b/src/export/render.rs @@ -61,6 +61,7 @@ fn render_frame( FrameItem::Meta(meta, _) => match meta { Meta::Link(_) => {} Meta::Elem(_) => {} + Meta::PageNumbering(_) => {} Meta::Hide => {} }, } diff --git a/src/model/introspect.rs b/src/model/introspect.rs index 31786d5bf..318088727 100644 --- a/src/model/introspect.rs +++ b/src/model/introspect.rs @@ -5,7 +5,7 @@ use std::num::NonZeroUsize; use super::{Content, Selector}; use crate::diag::StrResult; use crate::doc::{Frame, FrameItem, Meta, Position}; -use crate::eval::cast_from_value; +use crate::eval::{cast_from_value, Value}; use crate::geom::{Point, Transform}; use crate::model::Label; use crate::util::NonZeroExt; @@ -84,12 +84,18 @@ impl StabilityProvider { pub struct Introspector { pages: usize, elems: Vec<(Content, Position)>, + // Indexed by page number. + page_numberings: Vec, } impl Introspector { /// Create a new introspector. pub fn new(frames: &[Frame]) -> Self { - let mut introspector = Self { pages: frames.len(), elems: vec![] }; + let mut introspector = Self { + pages: frames.len(), + elems: vec![], + page_numberings: vec![], + }; for (i, frame) in frames.iter().enumerate() { let page = NonZeroUsize::new(1 + i).unwrap(); introspector.extract(frame, page, Transform::identity()); @@ -121,6 +127,9 @@ impl Introspector { let pos = pos.transform(ts); self.elems.push((content.clone(), Position { page, point: pos })); } + FrameItem::Meta(Meta::PageNumbering(numbering), _) => { + self.page_numberings.push(numbering.clone()); + } _ => {} } } @@ -184,6 +193,12 @@ impl Introspector { self.position(location).page } + /// Gets the page numbering for the given location, if any. + pub fn page_numbering(&self, location: Location) -> Value { + let page = self.page(location); + self.page_numberings.get(page.get() - 1).cloned().unwrap_or_default() + } + /// Find the position for the given location. pub fn position(&self, location: Location) -> Position { self.elems