Skip to content

CSL variable first-reference-note-number does not work #500

Description

@YDX-2147483647

first-reference-note-number is a CSL number variable. For note styles (as opposed to in-text styles), the number of a preceding note containing the first reference to the item should be assigned by the CSL processor.

In the example below, the CSL style displays first-reference-note-number for every citation, but Typst never assigns the variable.

<citation>
  <layout suffix=".">
    <text variable="title"/>
    <text value=", "/>
    <text variable="first-reference-note-number"/>
  </layout>
</citation>
@article{a, title = {A} }
@article{b, title = {B} }
A@a, B@b, A@a, A@a.
Full code
#set page(height: auto)
#set footnote(numbering: "")
#show footnote.entry: it => {
  counter(footnote).display(at: it.note.location())
  [ ]
  it.note.body
}
#set enum(numbering: "")

A@a, B@b, A@a, A@a.

= Expected output

+ A, 1.
+ B, 2.
+ A, 1.
+ A, 1.

#bibliography(
  title: [Actual output],
  bytes("
@article{a, title = {A} }
@article{b, title = {B} }
"),
  style: bytes(
    ```xml
    <?xml version="1.0" encoding="utf-8"?>
    <style xmlns="http://purl.org/net/xbiblio/csl" version="1.0" class="note">
      <info>
        <title/>
        <id/>
      </info>
      <citation>
        <layout suffix=".">
          <text variable="title"/>
          <text value=", "/>
          <text variable="first-reference-note-number"/>
        </layout>
      </citation>
      <bibliography>
        <layout>
          <text value="(irrelevant)"/>
        </layout>
      </bibliography>
    </style>
    ```.text,
  ),
)
Image

The CSL test suite contains several tests for first-reference-note-number, but according to the following logic, none of them can be tested in this repo.

hayagriva/tests/citeproc.rs

Lines 483 to 486 in 5a71313

let can_test = case.bib_entries.is_none()
&& case.bib_section.is_none()
&& case.citations.is_none()
&& case.citation_items.as_ref().is_none_or(|cites| {

This issue affects the gb-7714-2015-note style.

Additional tests

It looks like that the behaviour of first-reference-note-number and position differs from Typst and Hayagriva.

In the following test, the output of Hayagriva doesn't match the expectation either, but at least first-reference-note-number is not empty.

CSL test case
>>===== MODE =====>>
citation
<<===== MODE =====<<


>>===== RESULT =====>>
(omitted)
<<===== RESULT =====<<


>>===== CSL =====>>
<style 
      xmlns="http://purl.org/net/xbiblio/csl"
      class="note"
      version="1.0">
  <info>
    <id />
    <title />
    <updated>2009-08-10T04:49:00+09:00</updated>
  </info>
  <citation>
    <layout suffix=".">
      <text variable="title" suffix=", "/>
      <text variable="locator"/>
      <text value=", "/>
      <text variable="first-reference-note-number"/>
      <text value=" — Position: "/>
      <group delimiter=", ">
        <choose><if position="first"><text value="first"/></if></choose>
        <choose><if position="ibid"><text value="ibid"/></if></choose>
        <choose><if position="ibid-with-locator"><text value="ibid-with-locator"/></if></choose>
        <choose><if position="subsequent"><text value="subsequent"/></if></choose>
        <choose><if position="near-note"><text value="near-note"/></if></choose>
      </group>
    </layout>
  </citation>
</style>
<<===== CSL =====<<



>>===== CITATION-ITEMS =====>>
[
	[{ "id": "a" }],
	[{ "id": "b" }],
	[{ "id": "a" }],
	[{ "id": "a" }],
	[{ "id": "b" }]
]
<<===== CITATION-ITEMS =====<<


>>===== INPUT =====>>
[
	{
		"id": "a", 
		"title": "A"
	},
	{
		"id": "b", 
		"title": "B"
	}
]
<<===== INPUT =====<<


>>===== VERSION =====>>
1.0
<<===== VERSION =====<<
Equivalent Typst code
#set page(height: auto)

A@a, B@b, A@a, A@a, B@b.

#bibliography(
  bytes("
@article{a, title = {A} }
@article{b, title = {B} }
"),
  style: bytes(
    ```xml
    <?xml version="1.0" encoding="utf-8"?>
    <style
          xmlns="http://purl.org/net/xbiblio/csl"
          class="note"
          version="1.0">
      <info>
        <id />
        <title />
        <updated>2009-08-10T04:49:00+09:00</updated>
      </info>
      <citation>
        <layout suffix=".">
          <text variable="title" suffix=", "/>
          <text variable="locator"/>
          <text value=", "/>
          <text variable="first-reference-note-number"/>
          <text value=" — Position: "/>
          <group delimiter=", ">
            <choose><if position="first"><text value="first"/></if></choose>
            <choose><if position="ibid"><text value="ibid"/></if></choose>
            <choose><if position="ibid-with-locator"><text value="ibid-with-locator"/></if></choose>
            <choose><if position="subsequent"><text value="subsequent"/></if></choose>
            <choose><if position="near-note"><text value="near-note"/></if></choose>
          </group>
        </layout>
      </citation>
      <bibliography>
        <layout>
          <text value="(irrelevant)"/>
        </layout>
      </bibliography>
    </style>
    ```.text,
  ),
)

Expected output:

A, 1 — Position: first.
B, 2 — Position: first.
A, 1 — Position: subsequent, near-note.
A, 1 — Position: ibid, subsequent, near-note.
B, 2 — Position: subsequent, near-note.

Output of Typst v0.15.1:

A, — Position: first.
B, — Position: first.
A, — Position: subsequent.
A, — Position: ibid, subsequent.
B, — Position: subsequent.

Output of cargo test --features csl-json local for Hayagriva:

A, 1 — Position: first.
B, 1 — Position: first.
A, 1 — Position: subsequent, near-note.
A, 1 — Position: ibid, subsequent, near-note.
B, 1 — Position: subsequent, near-note.

Update on 2026-07-21

When testing against the CSL test suite, the note_number passed to Hayagriva is always Some(1). As a result, Hayagriva always resolves first-reference-note-number to 1.

Some(1),

Changing Some(1) to Some(n + 1) would make the output of Hayagriva matches the expectation.
However, citeproc-pass.txt doesn't change, because all relevant test cases are excluded by tests/citeproc.rs.

diff --git a/src/csl/mod.rs b/src/csl/mod.rs
index 6221859..0005d64 100644
--- a/src/csl/mod.rs
+++ b/src/csl/mod.rs
@@ -2532,7 +2532,7 @@ impl<'a> CiteProperties<'a> {
 /// citation or bibliography entry.
 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
 struct CertainCiteProperties {
-    /// The number of the footnote this citation appears in.
+    /// The number of the footnote this citation appears in, starting at 1.
     ///
     /// We can determine this because it depends on citation order only. May be
     /// none if the current style is in-text.
diff --git a/tests/citeproc.rs b/tests/citeproc.rs
index c827311..ad81d69 100644
--- a/tests/citeproc.rs
+++ b/tests/citeproc.rs
@@ -541,7 +541,7 @@ where
     let mut driver: BibliographyDriver<'_, csl_json::Item> = BibliographyDriver::new();
     let mut output = String::new();
     if let Some(cites) = &case.citation_items {
-        for cite in cites {
+        for (n, cite) in cites.into_iter().enumerate() {
             driver.citation(CitationRequest::new(
                 cite.iter()
                     .map(|i| {
@@ -572,7 +572,7 @@ where
                 &style,
                 None,
                 locales,
-                Some(1),
+                Some(n + 1),
             ));
         }
     } else {

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions