Format specific authors with Bold font in bibliography created with Quarto
Asked Answered
D

2

5

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:

&lt;b&gt;Skeeter&lt;/b&gt;

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

Dooryard answered 3/6, 2023 at 0:6 Comment(1)
Maybe this could be an option for html.Scop
K
5

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

rendered output

Kirovograd answered 8/6, 2023 at 8:11 Comment(0)
B
1

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>

The native format for CSL styles isnt' Bib(La)TeX but CSL JSON. Quarto is able to read CSL JSON as input, so if you are generating the input, e.g., from Zotero (which can export that format) you could then indeed use HTML markup such as <b></b> to bold author names.

If you want to start with BibTeX/.bib, though, you should bold the authors using TeX markup, i.e. author = \textbf{Skeeter}. I haven't tested this with quarto, but if it does proper conversion to CSL JSON internally, that should work.

Bowlin answered 4/6, 2023 at 3:6 Comment(1)
Thanks! This does work if you want to bold a given word in a title. But unfortunately, it doesn't work for authors because of the way the author names are handled - parsing, abbreviating, and sorting :( For now - my solution is to "hack" that involves outputting the .qmd file as a .md file, having another .qmd file with an embedded python code block read it, re-format the selected author names and save to a third .qmd file which is then rendered with the desired author names formatted in bold. I'll post the procedure as an answer if turns out there isn't a better way to do it.Dooryard

© 2022 - 2024 — McMap. All rights reserved.