typst
typst copied to clipboard
Multiple bibliographies
I am really excited to get to use typst, it looks great so far!
However, for larger documents it would be really helpful to have multiple bibliographies, e.g. to categorize publications into books, journals, etc. or to separate own prior publications from other peoples' works. This would be something like the bibunit
or chapterbib
packages in LaTeX. Unfortunately I cannot assign labels, but the [proposed Label] would be feature request
we need it for PhD too
I too have discovered that there can be only one #bibliography
, but its path argument can be an array, so you can include multiple bibliographies. However, there can't be any duplicate keys (if there are duplicate keys, I think it would probably make sense for later definitions to override earlier ones; then you could list the "most definitive" bibliographies last).
Something like that (in latex) would be nice indeed:
\printbibliography[heading=subbibliography, title={Articles}, type=article]
\printbibliography[heading=subbibliography, title={Reports}, nottype=online, nottype=article]
\printbibliography[heading=subbibliography, title={Internet}, type=online]
One more vote for this! I got 80% of the way through rebuilding my CV in typst, which is a dream compared to LaTeX, but this might kill the effort :-/. I'm looking into manually building building bibliographies from the yaml, but I am not quite confident enough in the syntax to have high hopes there.
An interim solution (though I'm not sure if it's actually easier to implement) would be to enable the style
of a #cite
call to be the full reference. In other words, instead of
== Some header
@label1
@label2
== Other header
@label3
#bibliography[...]
Giving
Maybe one could do something like
== Some header
#cite("label1", "label2", style: "full")
== Other Header
#cite("label3", style: "full")
To get
Some header
[1] M. C. Woodruff, K. S. Bonham, et al., “Chronic inflammation, neutrophil activity, and autoreactivity splits long Covid,” Nature Commun., vol. 14, no. 1, Jul. 2023, doi: 10.1038/ s41467-023-40012-7. [Online]. Available: https://doi.org/10.1038/s41467-023-40012-7
[2] A. A. Schoenborn, S. M. Yannarell, et al., “Microclimate is a strong predictor of the native and invasive plant-associated soil microbiome on san cristóbal island, galápagos archipelago,” Environmental Microbiology, Mar. 2023, doi: 10.1111/1462-2920.16361. [Online]. Available: https://doi.org/10.1111/1462-2920.16361
Other Header
[3] L. Tso, K. S. Bonham, A. Fishbein, S. Rowland, and V. Klepac-Ceraj, “Targeted high-resolution taxonomic identification of Bifidobacterium longum subsp. infantis using human milk oligosaccharide metabolizing genes,” Nutrients, vol. 13, no. 8, p. 2833, Aug. 2021, doi: 10.3390/ nu13082833. [Online]. Available: https://doi.org/10.3390/nu13082833
Is there a way to call hayagriva within typst? Something like my workaround is already possible with the hayagriva CLI:
❯ hayagriva testcite.yaml reference
Joe Schmoe, and Jane Doe. 2042. “This Is a Really Fascinating Title.” FEBS Journal. https://doi.org/10.1111/j.1742-4658.2010.07623.x.
Me Myself, and And Eye. 2001. “Boring Title,” https://doi.org/10.2222/j.x.
❯ hayagriva testcite.yaml reference --select "!misc"
Joe Schmoe, and Jane Doe. 2042. “This Is a Really Fascinating Title.” FEBS Journal. https://doi.org/10.1111/j.1742-4658.2010.07623.x.
❯ hayagriva testcite.yaml reference --select "!misc" --style ieee
Joe Schmoe, and Jane Doe, “This is a really fascinating title,” FEBS J., 2042, doi: 10.1111/j.1742-4658.2010.07623.x. [Online]. Available: https://doi.org/10.1111/j.1742-4658.2010.07623.x
I just hacked this together using the #yaml
function:
#let refs(contents) = {
for (tag, fields) in contents {
let auth_n = fields.author.len()
let auths = ()
if auth_n > 4 {
auths = fields.author.slice(0, count:4)
[#auths.join(", ") _et. al._]
} else {
auths = fields.author
auths.join(", ", last: " and ")
}
[, "#fields.title.value"]
[. #emph(fields.parent.at(0).title)]
[. (#fields.date)]
[ doi: #link(fields.url, fields.doi)]
linebreak()
}
}
#refs(
yaml("citations.yaml")
)
gives
OK, last one - this allows you to pass an array of citation keys, and pass a tag to include:
#let refs(file, entries: (), tag: none) = {
if entries.len() == 0 {
entries = yaml(file).keys()
}
for (entry, fields) in yaml(file) {
if entry not in entries {
continue
}
if not tag == none {
if "tags" not in fields or tag not in fields.tags {
continue
}
}
if "tags" in fields and "cofirst" in fields.tags {
[\* ]
}
if "tags" in fields and "corresponding" in fields.tags {
[#sym.dagger ]
}
let auth_n = fields.author.len()
let auths = ()
if auth_n > 4 {
auths = fields.author.slice(0, count:4)
[#auths.join(", ") _et. al._]
} else {
auths = fields.author
auths.join(", ", last: " and ")
}
[, "#eval("[" + fields.title.value + "]")"]
[. #emph(fields.parent.at(0).title)]
[. (#fields.date)]
[ doi: #link(fields.url, fields.doi)]
linebreak()
}
}
So you can do eg #refs("citations.yaml", tag: "preprint")
or #refs("citations.yaml", entries: ("entry1", "entry2"))
(or a combination.
It also has some logic to add a star at the beginning if you have the tag "cofirst", or a dagger if you have the tag "corresponding"
we need it for PhD too
Just wanted to confirm.
I am trying to reproduce my PhD thesis LaTex template in typst, but it was a necessity to have bibliographies for each chapter and numbering of references needed to start from 1 again.
Would be really nice feature to have.
Multiple bibliographies could meaning:
- the using of the different files.
- or by having a bibliography for each chapter with reference numbers that start from 1 in each chapter
so what's means Multiple bibliographies
?
@ayoubelmhamdi I think you can already use multiple bibliography files by passing an array to #bibliography
.
I currently do this using the lua filters for multiple bibliographies in Quarto v1.3. I haven't tested them in Typst/Quarto v1.4 yet, but I hope they'll work here, too.
- https://github.com/pandoc-ext/multibib to include multiple bib files as inputs
- https://github.com/pandoc-ext/section-bibliographies to print one bibliography per chapter/section
I wanted to add a page just with personal publications using the full: true
and adding a multiple bib files as an array will list out everything that is not part of my publications.
Is this being considered by the development team? Multiple bibliographies are a basic feature of any academic work.
@bcdavasconcelos It will be supported in the future, but I can't yet say when.
Hey all,
Seeing as I want to also be able to write my PhD dissertation in Typst, I figured I might come up with a way to allow multiple bibliographies in Typst, by using a Typst library. With multiple bibliographies, I intend to mean that every chapter has their own set of references and citations throughout the manuscript can correctly link to the desired bibliography. The project is still very young, but works relatively well. It only supports rudimentary APA style citation, but one can easily add their preferred citation style in the library itself.
https://github.com/jrihon/multi-bibs
Hopefully this can be of use to someone else too, until the real deal comes along!
Hey all,
Seeing as I want to also be able to write my PhD dissertation in Typst, I figured I might come up with a way to allow multiple bibliographies in Typst, by using a Typst library. With multiple bibliographies, I intend to mean that every chapter has their own set of references and citations throughout the manuscript can correctly link to the desired bibliography. The project is still very young, but works relatively well. It only supports rudimentary APA style citation, but one can easily add their preferred citation style in the library itself.
https://github.com/jrihon/multi-bibs
Hopefully this can be of use to someone else too, until the real deal comes along!
It's great to have a bibliography on every chapter, but it seems that you control the user on how to organize our project, ~that's a bad idea~.
rootdirectory/
- chapters/
- CHAPTER_X/
mod.typ, bibliography_X.yml
- CHAPTER_Y/
mod.typ, bibliography_Y.yml
- lib/
multi-bibs.typ
It's great to have a bibliography on every chapter, but it seems that you control the user on how to organize our project, that's a bad idea.
rootdirectory/ - chapters/ - CHAPTER_X/ mod.typ, bibliography_X.yml - CHAPTER_Y/ mod.typ, bibliography_Y.yml - lib/ multi-bibs.typ
As stated as a disclaimer and in the TODO
section, I am very much aware of the rigidity of the project. One of the key problems is that the #yaml()
function infers a path to your .yml
file and prepends it to the given path. I've figured out that by passing an absolute path to the .yml
directly, typst prepend the path with the path to the root directory of the project. This Path inference is generally quite annoying, but I understand its place in the Typst atm. This path inference is also very variable and changes on the type of path you pass into the argument of the function, hence its fickle nature. That is why, for now, I decided to make a bit rigid, just to get it working.
But hey, it's open source, feel free to change it yourself if you don't like it. I only figured out a relatively viable solution to a problem I have, and replying with a that's a bad idea
to a glaring issue I acknowledged myself is just not productive.
If you use IEEE reference format (or something similar that goes by number), this will work. The only downside is it won't merge adjacent references, but you can accommodate this by using cite
rules instead of ref
rules.
#show bibliography: none
#bibliography("refs.bib")
// Keep track of all references, clearing every time a new heading is shown
#let section-refs = state("section-refs", ())
// Add bibliography references to the current section's state
#show ref: it => {
if it.element != none {
// Citing a document element like a figure, not a bib key
// So don't update refs
it
return
}
section-refs.update(old => {
if it.target not in old {
old.push(it.target)
}
old
})
locate(loc => {
let idx = section-refs.at(loc).position(el => el == it.target)
"[" + str(idx + 1) + "]"
})
}
// Print the "per-section" bibliography
#let section-bib() = locate(loc => {
let ref-counter = counter("section-refs")
ref-counter.update(1)
show regex("^\[(\d+)\]\s"): it => [
[#ref-counter.display()]
]
for target in section-refs.at(loc) {
block(cite(target, form: "full"))
ref-counter.step()
}
})
// Clear the previously stored references every time a level 1 heading
// is created.
#show heading.where(level: 1): it => {
section-refs.update(())
it
}
= First Section
My reference @plucked-string and another @plot
#section-bib()
= Second Section
Another reference @plucked-string-extensions @plot @plucked-string-extensions @plucked-string
#section-bib()
if you use IEEE reference format (or something similar that goes by number), this will work. The only downside is it won't merge adjacent references, but you can accommodate this by using cite rules instead of ref rules.
One of the big utilities is being able to link the citation to the references, by making hyperlinks between them (link, label etc.) . What I am wondering here is the following : what if you are citing a specific reference, sourced from a single bibliography, and using that citation in multiple chapters, thereby creating the multiple instances of the same label. Wouldn't that create ambiguous hyperlinks? Typst will not compile if that is the case .. I ran into this issue, so I created unique labels in the Referencelist, by splitting the bibliography per chapter for my specific issue.
basically we need to reproduce the biblatex behaviour here https://www.overleaf.com/learn/latex/Questions/Creating_multiple_bibliographies_in_the_same_document
we need 1 new concept refsection. Then a #bibliography()
only print ref list inside the refsection. We must to have this to make typst really useful. If I only write a few page document, latex is not that slow.
In more advanced version, the refsection should be able to nested. So at the end we can also have a global ref list. But I think it is ok to not support this option. Usually it is quite redundant.
#show heading.where(level: 1): it => { section-refs.update(()) it }
There could be some improvements:
- the
section-refs.update
action can be merged into each section-bib() function. - There seems to be a bug, the ref id cannot be obtained by using
len
, rather, it should be obtained by.position
.
I made some modification as
//modified based on
//https://github.com/typst/typst/issues/1097#issuecomment-1928525350
#show bibliography: none
#bibliography("ref.bib")
// Keep track of all references, clearing every time a new heading is shown
#let section-refs = state("section-refs", ())
// Add bibliography references to the current section's state
#show ref: it => {
if it.element != none {
// Citing a document element like a figure, not a bib key
// So don't update refs
it
return
}
section-refs.update(old => {
if it.target not in old {
old.push(it.target)
}
old
})
//I think the following is how the reference index should be obtained correctly.
let get_ref_id(loc)={
//str(section-refs.at(loc).len())
str(section-refs.at(loc).position(x=>(x==it.target))+1)
}
locate(loc => {
"[" + get_ref_id(loc) + "]"
})
}
// Print the "per-section" bibliography
#let section-bib() = locate(loc => {
//https://github.com/typst/typst/issues/1097
let ref-counter = counter("section-refs")
ref-counter.update(1)
show regex("^\[(\d+)\]\s"): it => [
[#ref-counter.display()]
]
for target in section-refs.at(loc) {
block(cite(target, form: "full"))
ref-counter.step()
}
section-refs.update(())
})
@astrojhgu
the section-refs.update action can be merged into each section-bib() function
I thought of this too; the reason it is separated is because people often see the code and ask "how can I modify it so that references are per-section", or "how can I incorporate a call to section-bib
automatically"? This type of answer plants the seed of how to make your own adjustments as needed (in our case, a small introduction of other concepts beyond the answer itself, like tying into other show rules).
There seems to be a bug
You're absolutely right! I've updated my answer above to include these adjustments and verified with a new screenshot.
Would you consider surrounding your code block inside a <details>
... </details>
wrapper now that the code block is updated? It will help ensure there is just one community-maintained snippet.
Would you consider surrounding your code block inside a
<details>
...</details>
wrapper now that the code block is updated? It will help ensure there is just one community-maintained snippet.
Sure, I have modified the code block.
(not sure if I did it correctly, as I'm not very familiar with the <details />
tag).
Sure, I have modified the code block. (not sure if I did it correctly, as I'm not very familiar with the
<details />
tag).
Just type /d
at the start of a new line and then press Enter (in the web version of GitHub). It's called a "slash command". For convenience.
https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-collapsed-sections
https://docs.github.com/en/issues/tracking-your-work-with-issues/about-slash-commands
(I fixed the formatting of the <details />
block. It needs a newline between <details>
and the code block.)
For the peace of mind, I add the newline above and below the code block.
(The /details
slash command also adds an unnecessary in my opinion p
tag and a whitespace after </details>
, so it's kinda useful, and I hate it at the same time.)
If you use IEEE reference format (or something similar that goes by number), this will work. The only downside is it won't merge adjacent references, but you can accommodate this by using
cite
rules instead ofref
rules.#show bibliography: none #bibliography("refs.bib") // Keep track of all references, clearing every time a new heading is shown #let section-refs = state("section-refs", ()) // Add bibliography references to the current section's state #show ref: it => { if it.element != none { // Citing a document element like a figure, not a bib key // So don't update refs it return } section-refs.update(old => { if it.target not in old { old.push(it.target) } old }) locate(loc => { let idx = section-refs.at(loc).position(el => el == it.target) "[" + str(idx + 1) + "]" }) } // Print the "per-section" bibliography #let section-bib() = locate(loc => { let ref-counter = counter("section-refs") ref-counter.update(1) show regex("^\[(\d+)\]\s"): it => [ [#ref-counter.display()] ] for target in section-refs.at(loc) { block(cite(target, form: "full")) ref-counter.step() } }) // Clear the previously stored references every time a level 1 heading // is created. #show heading.where(level: 1): it => { section-refs.update(()) it } = First Section My reference @plucked-string and another @plot #section-bib() = Second Section Another reference @plucked-string-extensions @plot @plucked-string-extensions @plucked-string #section-bib()
Does anyone know how to modify it for apa references and adding page numbers?
I tried adjusting it but I can't figure out a way to make page numbers work. I currently cite works like this: @cognitive-neuroscience[s.~110]
#show bibliography: none
#bibliography("bibliography.yml", title: "Referenser", style: "apa")
// Keep track of all references, clearing every time a new heading is shown
#let section-refs = state("section-refs", ())
// Add bibliography references to the current section's state
#show ref: it => {
if it.element != none {
// Citing a document element like a figure, not a bib key
// So don't update refs
it
return
}
section-refs.update(old => {
if it.target not in old {
old.push(it.target)
}
old
})
locate(loc => {
let ref_pos = section-refs.at(loc).position(el => el == it.target)
let ref_target = section-refs.at(loc).at(ref_pos)
let apa_normal_citation = cite(ref_target, form: "normal")
apa_normal_citation
})
}
// Print the "per-section" bibliography
#let section-bib() = locate(loc => {
let ref-counter = counter("section-refs")
ref-counter.update(1)
show regex("^\[(\d+)\]\s"): it => [
[#ref-counter.display()]
]
for target in section-refs.at(loc) {
block(cite(target, form: "full"))
ref-counter.step()
}
})
// Clear the previously stored references every time a level 1 heading
// is created.
#show heading.where(level: 1): it => {
section-refs.update(())
it
}
This would be very helpful for dissertations, where in some cases references and (own) publications are required to be listed separately. It would be wonderful if this worked as expected:
#bibliography("references.bib")
#pagebreak(weak: true)
#bibliography("publications.bib", full: true, title: "List of Publications")
I want to flag up a repo with a minimal working example for an approach that basically solves this issue (at least for my needs).
https://github.com/isometricneko/typst-example
I'm not fully understanding how it works, but it allows me to see the bibliography in chapter-level and document-level previews, everything compiles as expected, and VS Code does not complain about using citations without a bibliography either.
The basic idea (from the author) is:
you can use
state
to store the#bibliography
for each of the subfiles and update the value of thestate
tonone
in the main file
So well done to @isometricneko !
The only situation I have found this approach creating problems is when using pandoc to transpile to .tex.