I've put together a webpage for my research group using quarto - we have a page listing the lab's publications
I've adapted a .csl file to fit most of my formatting needs (eg. listing chronologically instead of alphabetically), but I'm stuck on one last thing. We want to format authors from our lab in bold - while leaving external collaborators in normal font-weight. Its easy enough to set all authors to render in bold, but from what I can gather there isn't a way to define a list of specific strings that should be formatted in bold?
I've found some documentation that suggests using rich text in my .bib file. However, I'm finding that if I put something like this in the .bib file:
author = <b>Skeeter</b>
When I run quarto render
the formatting is output in the .html file as:
<b>Skeeter</b>
so it shows up literally as <b>Skeeter</b>
in the document rather than Skeeter.
My question is whether anyone is aware of a work around that I could employ to achieve this goal? Is there a way to have quarto format specific text values in bold or a way to have a simple script run every time quarto render
is called to format things after the fact? Ideally I'd like to render the publications.qmd file to both .html and .pdf formats - so running a completely separate script after rendering is complete to edit docs/Publications.html would only solve half the problem - the .pdf would still be rendered without the bold formatting.
edit: As mentioned in an answer below - \textbf{Skeeter}
would be more appropriate for a .bib file. And this does work for formatting words in titles - but unfortunately does not work for authors because of the way they are parsed, sorted, etc. I also tried a csl json format instead, but same issue - adding <b>Skeeter</b>
does not result in Skeeter
The Solution: @shafee provided a great solution below that works like a charm. Create a bold-author.lua filter, save it it the main directory, and specify the author names you want to bold in the YAML header, eg.
filters:
- section-bibliographies
- bold-author.lua
bold-auth-name:
- family: Knox
given: S. H.
- family: Skeeter
given: J.
One minor quirk however - it skipped over authors with two given names, ie. a first & middle initial for authors with two given names, so
Skeeter, J., & Knox, S. H. (2023, April). Ongoing and Proposed Research in the Burns Bog Ecological Conservancy Area.
Changing to:
bold-auth-name:
- family: Knox
given: S.
would give: Skeeter, J., & Knox, S. H. (2023, April). Ongoing and Proposed Research in the Burns Bog Ecological Conservancy Area.
So I updated the provided lua filter slightly to:
str = pandoc.utils.stringify
local function highlight_author_filter(auths)
return {
Para = function(el)
if el.t == "Para" then
for k,_ in ipairs(el.content) do
for key, val in ipairs(auths) do
local first_part = val.family .. ","
local full = val.family .. ", " .. val.given
given_initials = {}
for w in val.given:gmatch("%S+") do
table.insert(given_initials,w)
end
if #given_initials == 1 then
if el.content[k].t == "Str" and el.content[k].text == first_part
and el.content[k+1].t == "Space" and el.content[k+2].t == "Str"
and el.content[k+2].text:find(given_initials[1]) then
local _,e = el.content[k+2].text:find(given_initials[1])
local rest = el.content[k+2].text:sub(e+1)
el.content[k] = pandoc.Strong { pandoc.Str(full) }
el.content[k+1] = pandoc.Str(rest)
table.remove(el.content, k+2)
end
elseif #given_initials == 2 and #el.content >= k+4 and el.content[k+4].text ~= nil then
if el.content[k].t == "Str" and el.content[k].text == first_part
and el.content[k+1].t == "Space" and el.content[k+2].t == "Str"
and el.content[k+2].text:find(given_initials[1])
and el.content[k+4].text:find(given_initials[2]) then
local _,e = el.content[k+4].text:find(given_initials[2])
local rest = el.content[k+4].text:sub(e+1)
el.content[k] = pandoc.Strong { pandoc.Str(full) }
el.content[k+1] = pandoc.Str(rest)
table.remove(el.content, k+4)
table.remove(el.content, k+3)
table.remove(el.content, k+2)
end
end
end
end
end
return el
end
}
end
local function get_auth_name(auths)
return {
Meta = function(m)
for key, val in ipairs(m['bold-auth-name']) do
local auth = {
["family"] = str(val.family),
["given"] = str(val.given)
}
table.insert(auths, auth)
end
end
}
end
local function highlight_author_name(auths)
return {
Div = function(el)
if el.classes:includes("references") then
return el:walk(highlight_author_filter(auths))
end
return nil
end
}
end
function Pandoc(doc)
local bold_auth_name = {}
doc:walk(get_auth_name(bold_auth_name))
return doc:walk(highlight_author_name(bold_auth_name))
end
Probably a more elegant way to do it, but I'm not super familiar w/ Lua. Just providing here for anyone who needs it. Again all credit to @shafee :D
You can use a lua filter to highlight specific Author surname and initials while rendering the Pubs.qmd
document (No need to do multiple rendering and create other files)
Use the bold-author.lua
filter after the section-bibliographies
filter. And then specify the Author's family
and given
under the bold-auth-name
yaml key (got the idea of key-names family
and given
from lab_publications.json file).
Pubs.qmd
---
title: "Publications"
format: html
bibliography: lab_publications.json
section-bibs-bibliography: lab_publications.json
section-bibs-level: 2
citeproc: False
csl: american-geophysical-union.csl
filters:
- section-bibliographies
- bold-author.lua
bold-auth-name:
- family: Skeeter
given: J.
- family: Satriawan
given: T.
---
## Theses
::: {.hidden}
@satriawan_interannual_2022
@russell_increased_2021
@nyberg_impacts_2021
:::
## Research Talks & Poster Presentations
::: {.hidden}
@skeeter_ongoing_2023
@knox_ubc_2023
@ng_characterizing_2022
@lu_investigating_2022
@satriawan_interannual_2022_a
@satriawan_interannual_2022_b
@satriawan_interannual_2021
@satriawan_interannual_2020
@nyberg_impacts_2020
:::
bold-author.lua
The actual implementation of this filter is based on this answer, I have then further modified it so that it works for multiple authors and the family
and given
values from the quarto document are passed to the filter correctly.
str = pandoc.utils.stringify
local function highlight_author_filter(auths)
return {
Para = function(el)
if el.t == "Para" then
for k,_ in ipairs(el.content) do
for key, val in ipairs(auths) do
local first_part = val.family .. ","
local second_part = "^" .. val.given
local full = val.family .. ", " .. val.given
if el.content[k].t == "Str" and el.content[k].text == first_part
and el.content[k+1].t == "Space"
and el.content[k+2].t == "Str" and el.content[k+2].text:find(second_part) then
local _,e = el.content[k+2].text:find(second_part)
local rest = el.content[k+2].text:sub(e+1)
el.content[k] = pandoc.Strong { pandoc.Str(full) }
el.content[k+1] = pandoc.Str(rest)
table.remove(el.content, k+2)
end
end
end
end
return el
end
}
end
local function get_auth_name(auths)
return {
Meta = function(m)
for key, val in ipairs(m['bold-auth-name']) do
local auth = {
["family"] = str(val.family),
["given"] = str(val.given)
}
table.insert(auths, auth)
end
end
}
end
local function highlight_author_name(auths)
return {
Div = function(el)
if el.classes:includes("references") then
return el:walk(highlight_author_filter(auths))
end
return nil
end
}
end
function Pandoc(doc)
local bold_auth_name = {}
doc:walk(get_auth_name(bold_auth_name))
return doc:walk(highlight_author_name(bold_auth_name))
end
The only limitation of this filter I am aware of is that its only works with the section-bibliographies
filter
rendered output