Creating Quarto Journal Article Templates

quarto

A long walk through creating Quarto templates for journal articles.

Author
Published

July 1, 2023

Background

This post is largely written for my future self. If you’re not me and you’ve ended up here, I do hope that this is helpful for you, but it may fall below or above your current experience. I’ll include informative links as I think of them to help with the latter. For the former, this post may be too specific to my process to be helpful.

Your Template Background

As with all blog posts, I’m assuming you both know everything and nothing. You have every clue how Quarto, Markdown, LaTeX, pandoc, and Lua work and simultaneously have never used anything but Microsoft Word.

More realistically, you need some experience with one of Quarto, RMarkdown, or Markdown and definitely some experience with LaTeX.

You needn’t be an expert on the first part, but if you’re new to LaTeX, take some time to write a few documents before trying to build templates that rely on it for pdf-output.

My Template Background

I’ve made a handful of templates for Quarto and RMarkdown. A few of them are public, but many of them are hacky one-off solutions to some problem. I’m focused here on the more-focused public template, where you want it to work in general for a journal’s set of problems.

Perhaps most importantly, I’ve got about eight years of self-taught LaTeX experience. That’s good for me and bad for you (or maybe good for you and bad for me). My solutions to problems often (1) produce correct output and (2) are sub-optimal. That’s not the best, but if you care about (1) more, then, hey, we’ve got some work to do.

For full context: This post was mostly written while I was making a PNAS template for Quarto. Previously, I used the rticles::pnas_template() but (at the time of writing) I was trying to get a better sense of how Quarto built things. So, I have a sense of what it should look like, but no guarantee of understanding how the details work.

Making the Template

First things first, we need to initialize the template. We can do that in the terminal with:

quarto create extension journal

It’ll prompt you to give it a name. Names tend to be short and normally match a common journal abbrevation. Quarto has a list of templates, as does Awesome Quarto. Some names I’ve used:

journal name
APSR apsr
PNAS pnas
PA, PSRM, BJPS, … cambridge-medium
Scientific Data scientific-data

As you can see, some are obvious. The Cambridge medium one tripped me up a bit, as it was intended for at least five journals. As such, I just named it for the template and added text to make it clear what journals it was for.

Now, once you have a name, it creates a new R project named for the name you provided. You can open that and give the structure a look.

Note

For the rest of this post, I’ll use building a PNAS template as the example. pnas can be replaced with the name you chose above.

On creation, the file structure should look something like this:

.
├── bibliography.bib
├── pnas.Rproj
├── README.md
├── template.qmd
└── _extensions
    └── pnas
        ├── header.tex
        ├── pnas.lua
        ├── styles.css
        └── _extension.yml

For a journal template, we’re going to have to augment the _extensions/pnas folder. For now some quick bits of information on included files:

  • README.md is the general readme for the whole template. It’ll hold installation instructions, option documentation, and anything else you need users to know.
  • template.qmds is where our example template goes.
  • bibliography.bib is a placeholder bibtex file.
  • _extensions/pnas/_extension.yml will hold the metadata for the template. This tells Quarto which files to use and such.
    • _extensions/pnas/header.tex is included in the default template and will be inserted in the header of the LaTeX template.
  • _extensions/pnas/styles.css can probably be deleted at this point, unless you want to make a website version of the template, but you probably aren’t if you’re making a journal template.
  • _extensions/pnas/pnas.lua is the default Lua filter. Lua is powerful, but only necessary for more complicated features.

The README

The first thing I edit is the README. It can’t be finished yet, but I find it helpful to organize right away.

Using RStudio’s find+replace, I change the github organization placeholder to my GitHub account name (christopherkenny). Then, using the same tool, I replace the file name and title with template name from before. (If you now feeling anxious about the name, this is the right time to fix it before doing anything.) Then, I remove the (now complete) TODOs for those.

In the “Example” section, I then start to add more info. First, I add some template code to include a screenshot.

<!-- pdftools::pdf_convert('template.pdf',pages = 1) 
![[template.qmd](template.qmd)](template_1.png) -->

Once the template is finished, I’ll run the first line in R and uncomment the second. Including a screenshot is inspired by Cory McCartan’s pre-print template. I think it’s a nice touch.

The options section is helpful for the end user, but may not be super clear right now. I think it is best to hold off on that and figure it out later.

For now, the last thing I’ll do is add a bit of information on licensing. Odds are good that I’m working off of some official or officially endorsed template. Even if it isn’t official, you want to give credit to the LaTeX wizardry going on behind the scenes. My license section typically takes the form, using {glue} syntax for placeholders:

## License
This modifies the {Owner} {What} Template, available at <{some_link}>.
The original template is licensed under the [{license_name}]({license_link}).
{any_other_required_notes_about_modifications}

Adding LaTeX Files into the Template

Before you dive into building the Quarto part of the template, I’ll next download any relevant files for the LaTeX part of the template. For the PNAS template, I downloaded the source files from Overleaf.

From the template, I extract all of the LaTeX-related files (.bst, .sty, .cls, and in this case .ldf files). Those files go into the _extensions/pnas folder. Any similar files that we want to be available to the template itself should go there.

Any files that are necessary for demonstrating the template can go into a higher-level folder. For example, in the PNAS template, they use a frog as the placeholder image. I make a folder called figs/ that can hold such things.

At this point, my file structure looks like:

.
├── bibliography.bib
├── figs
│   └── frog.pdf
├── pnas.Rproj
├── README.md
├── template.qmd
└── _extensions
    └── pnas
        ├── header.tex
        ├── jabbrv-ltwa-all.ldf
        ├── jabbrv-ltwa-en.ldf
        ├── jabbrv.sty
        ├── pnas-new.bst
        ├── pnas-new.cls
        ├── pnas.lua
        ├── pnasresearcharticle.sty
        ├── styles.css
        └── _extension.yml

Putting the files here only does so much. We have to let the template know that we have them. This all happens in _extensions/pnas/_extension.yml

There are a pair of lines in the file telling it what to use for PDFs.

    pdf:
      include-in-header: header.tex

We’ll add some new instructions for the format-resources listing out the new files that we have.

    pdf:
      include-in-header: header.tex
      format-resources:
        - pnas-new.cls
        - pnas-new.bst
        - pnasresearcharticle.sty
        - jabbrv.sty
        - jabbrv-ltwa-all.ldf
        - jabbrv-ltwa-en.ldf

Editing template.qmd (Part 1: The YAML)

Once we’ve got all the files in place, it’s time to make some edits to template.qmd so that we can render it and see how we’re doing.

The first big part is setting up the Quarto YAML. This is where all of the information that we will later use in the template will go.

The first few lines of the document are standard, but we’ll make two changes to the below:

title: Pnas Template
format:
  pnas-pdf:
    keep-tex: true  
  pnas-html: default

First, I’ll give it a more fun title, here “Quarto Template for PNAS Submissions”. Second, I’m focused on making the pnas-pdf class, so I’ll remove the pnas-html: default line from line 6 (or so) of the YAML. For journals that need PDFs for submission, the extra type doesn’t seem worth the effort.

Authors and Affiliations YAML

Not all templates use all possible information about authors. But, there is a pretty extensive set of possible pieces of information.

We need to include every relevant piece of information that the journal template will use. If we don’t include everything, there might be weird empty spaces. If we over include, that’s okay, but some things will get ignored.

Unlike the rticles templates that RMarkdown used to use, Quarto has tried to build an exhaustive schema for people’s names and their affiliations. The schema are detailed extensively at https://quarto.org/docs/journals/authors.html.

If we peek at some recent PNAS articles, we can see what author and affiliation information are used.

Authors look something like

Author One\(^{a, c, 1}\), Author Two\(^{b, 1, 2}\), and Author Three^\(a\)

which is kind of a mess.

1 indicates that the authors contributed equally. 2 indicates the corresponding author. a,b,c are all affiliations.

Affiliations themselves are pretty standard here. They should get printed to look like

University, Department, City, State ZIP

With that much information, we can assemble the YAML for an author. To make sure we can see the difference in these, I’ll do set up the affiliations as:

  • a: Harvard University, Department of Government, Cambridge, MA 02138
  • b: Yale University, Department of Political Science, New Haven, CT 06511
  • c: Harvard University, Department of Statistics, Cambridge, MA 02138
author:
  - name: Author One
    affiliations:
      - name: Harvard University
        id: a
        department: Department of Government
        city: Cambridge
        state: MA
        postal-code: 02138
      - name: Harvard University
        id: c
        department: Department of Statistics
        city: Cambridge
        state: MA
        postal-code: 02138
    attributes:
      equal-contributor: true

Note here that Author One and Author Two contributed equally and Author Two is the corresponding author.

With that, we can build out the other authors.

  - name: Author Two
    affiliations:
      - name: Yale University
        id: b
        department: Department of Political Science
        city: New Haven
        state: CT
        postal-code: 06511
    attributes:
      equal-contributor: true
      corresponding: true
  - name: Author Three
    affiliations:
      - ref: a

Building out Author Two’s affiliation is very similar, we just add a corresponding: true under attributes along with their information. For Author Three, we can do a cool thing and reference affiliation a, since it’s the same.

Now at this point, I know I’ll also need the email for the corresponding author, so I’ll add an email: corresponding@email.com line to the Author Two chunk.

Custom YAML Components

The rest of the YAML varies immensely by journal. To figure out what we need, we have to look back at the LaTeX template. We want to identify anything where we either need to tell the template about options to use or replace filler text. To that point, I’ve pulled out each thing that I see as something we need to be controllable in the YAML.

\templatetype{pnasresearcharticle} % Choose template
% {pnasresearcharticle} = Template for a two-column research article
% {pnasmathematics} %= Template for a one-column mathematics article
% {pnasinvited} %= Template for a PNAS invited submission

It looks like we need a template_type argument to the YAML. Later, we can then list the options in the README.

  • pnasresearcharticle: Template for a two-column research article
  • pnasmathematics: Template for a one-column mathematics article
  • pnasinvited: Template for a PNAS invited submission

We should be aware though that these actually have different official templates. This gives an important choice:

  1. try to support it
  2. let it be set but make it clear that the template is optimized for pnasresearcharticle types
  3. don’t make it settable and just always use pnasresearcharticle

I tend to go for (2) since it might work really easily without changing anything. Aiming for (1) is commendable, but may open a new can of worms. Option (3) is really safe and might actually be the best option, but if it at least mostly works, then someone else trying to do the other type now has to start from scratch when they didn’t have to. Part of how I’ll approach this is to make it the default in LaTeX if nothing is specified.

\leadauthor{Lead author last name}

This is a pretty common request, as it’s used in the header of pages. To be consistent with other journal types, such as apsr, I’ll call this field runningauthor. It’s always better to be consistent, since it makes switching easier, but if I didn’t know about the other name, I probably would have called it lead_author.

\significancestatement{Authors must submit a 120-word maximum statement about the significance of their research paper written at a level understandable to an undergraduate educated scientist outside their field of speciality. The primary goal of the significance statement is to explain the relevance of the work in broad context to a broad readership. The significance statement appears in the paper itself and is required for all research papers.}

For this, we can just give a new argument called significance, since it is just a bunch of text.

\authorcontributions{Please provide details of author contributions here.}

For this, we can also make a new argument called author_contributions. It’s a bunch of text and readable is best.

\authordeclaration{Please declare any competing interests here.}

I tend to call this conflict_of_interest, as I’ve seen it that way in the old rticles days.

\keywords{Keyword 1 $|$ Keyword 2 $|$ Keyword 3 $|$ ...}

Keywords come up in many templates and are included by default in the automatically generated YAML as keywords.

\doi{\url{www.pnas.org/cgi/doi/10.1073/pnas.XXXXXXXXXX}}

This one looked like something that we might want to change, but is probably best to leave for now. With how publications and submissions work for PNAS, you won’t know the doi until after you’re done using the template.

\firstpage{12}
% Use \firstpage to indicate which paragraph and line will start the second page and subsequent formatting. In this example, there are a total of 11 paragraphs on the first page, counting the first level heading as a paragraph. The value {12} represents the number of the paragraph starting the second page. If a paragraph runs over onto the second page, include a bracket with the paragraph line number starting the second page, followed by the paragraph number in curly brackets, e.g. "\firstpage[4]{11}".

Now this one presents an interesting question. We could include a first_page argument. Though, the formatting is kinda tricky. We would need to support both \firstpage{12} and \firstpage[4]{11}. It seems to me that this would be a good thing to leave out of the YAML but use within the template.qmd, so that it’s obvious it should be used but doesn’t require multi-option handling. That is my preference and I can’t say for sure if this is best practice.

\acknow{Please include your acknowledgments here, set in a single paragraph. Please do not include any acknowledgments in the Supporting Information, or anywhere else in the manuscript.}

We can give this a name like acknowledgements, again looking to other templates to see what they use as names, since this is a pretty common thing to have.

Okay now that gives us pretty set of YAML option to add. They’ll look like:

runningauthor: "One, Two, and Three"
significance: |
  Authors must submit a 120-word maximum statement about the significance of their research paper written at a level understandable to an undergraduate educated scientist outside their field of speciality. The primary goal of the significance statement is to explain the relevance of the work in broad context to a broad readership. The significance statement appears in the paper itself and is required for all research papers.
author_contributions: "Please provide details of author contributions here."
conflict_of_interest: "Please declare any competing interests here."
keywords: [template, demo]
acknowledgements: | 
  Please include your acknowledgments here, set in a single paragraph. Please do not include any acknowledgments in the Supporting Information, or anywhere else in the manuscript.

As you can see, where applicable, I’m using the instructions as the placeholder text where applicable. This has the benefit of keeping the end-user instructions near the end user without them having to deep dive into the original LaTeX template.

The bad news: that was the easy part. The good news: the next part walks you through the hard part.

Use Git

If you aren’t already using Git at this point, now is a good time to run usethis::use_git(), initialize a repo, and commit what you’ve done. This is a good save point so that if you start playing with things for the next part, you can easily remove them if you break anything.

Using the YAML in LaTeX

Adding the arguments to the YAML doesn’t automatically change anything about the output. It makes variables available to Pandoc’s processing as the name of the YAML set. So, for example, if our YAML looked like:

title: Quarto Template for PNAS Submissions

then a variable called title would be available for us to use. We can extract its value with $title$.

Possibly the most common is to check if a value is set and then use it if it is. For something like title, where we want to pass it to the LaTeX function \title{}, we can do this with the following pattern.

$if(title)$
\title{$title$}
$endif$

The first and third lines here are how if statements are done. So, if the title is set then in your YAML, then the outputted .tex file will say:

\title{Quarto Template for PNAS Submissions}

For more details on how these types of variables work, take a look at Template syntax section of the Pandoc manual.

Most of the templating from here is going to be finding the correct place to evaluate a variable. To find those places, we have to introduce the idea of partials, which are short files that get injected into the bigger template.

LaTeX Partials

Partials represent little snippets of LaTeX that get joined in a specific order. Quarto is built off of a series of default partials. If you’re rendering a regular document without any of this templating, it passing through those. The defaults are very good: they cover lots of cases and do them in a smart, efficient way. The ideal is that you only have to change a small number of partials. Journals have all sorts of format choices that you want to match, so we will have to replace some.

As assumed before, you know some LaTeX. A familiar, stylized LaTeX document then looks something like:

% first line is the *doc-class*
\documentclass{article}

% then we have the header
% like where we load packages with \usepackage{}

% This next group of lines are the *title*
\title{Some Latex Document}
\author{Christopher Kenny}
\date{July 2023}

\begin{document}

% then we have *before-body*
\maketitle

The body of the document

% then we have *after-body*

% then we have the *before-bib*

The references
% then we have *biblio* which makes the bibliography

\end{document}

Each of the things in *s is a partial and the general place where it goes. There are more partials, like toc, a table of contents partial. There’s also a partial for pandoc, for some things that Pandoc needs for rendering from LaTeX. Odds are good that you don’t have to touch the Pandoc one!

A full description of Quarto partials is available in the Quarto documentation. This lists out all of the partials and a short description of what they do. The source files for the partials are on GitHub.

Any partials that we need will live in a partials folder, below where the _extension.yml folder lives. Each partial will be a .tex file. So, they will all be something like: _extensions/pnas/partials/*.tex. Each file that we add to this folder has to be listed in _extension.yml, which I’ll show below.

With the general idea of partials down, we can go in order from the top down and see what we need.

doc-class.tex

Okay, so first things first is the document class. In the template, it looks like this:

\documentclass[9pt,twocolumn,twoside]{pnas-new}

Arguably, we don’t need to change this one out. This whole partial will evaluate to one line. The default looks like this:

\documentclass[
$if(fontsize)$
  $fontsize$,
$endif$
$if(papersize)$
  $papersize$paper,
$endif$
$if(beamer)$
  ignorenonframetext,
$if(handout)$
  handout,
$endif$
$if(aspectratio)$
  aspectratio=$aspectratio$,
$endif$
$endif$
$for(classoption)$
  $classoption$$sep$,
$endfor$
]{$documentclass$}

I say that we don’t need to necessarily change this one because we could pass the following to the YAML.

fontsize: "9pt"
classoption:
 - twocolumn
 - twoside
documentclass: "pnas-new"

This would do the following:

  • $if(fontsize)$ is true, so return the fontsize (9pt) and include the ,.
  • $if(papersize)$ isn’t specified, so it’s false and nothing happens.
  • $if(beamer)$ isn’t specified, so it’s false and nothing happens.
  • $if(handout)$ isn’t specified, so it’s false and nothing happens.
  • $if(aspectratio)$ isn’t specified, so it’s false and nothing happens.

Then we get a for loop. This works like the if syntax from before. It takes each element of classoption (a length two vector with twocolumn and twoside), returns them followed by the seperator. The $sep$, $endfor$ syntax just says “hey this is the separator ,, a comma followed by a space.”

Finally, document class would be pnas-new. So that completes it as

\documentclass[9pt,twocolumn,twoside]{pnas-new}

There are at least three advantages to implementing this simple partial as a custom partial. First, we can hard code the documentclass to be just the class we want to support. This avoids weird errors related to incorrect class specifications by downstream users. Second, if we decide that something is important enough to elevate to an additional YAML argument, we can then do so and add it with a little if syntax. For example, the APSR has a special nonblind argument that is really important for the submission. Knowing that, I could then make it a YAML option here. Third, we can remove some of the arguments that aren’t relevant to our template, like aspectratio.

With that in mind, I’ll first trim the options:

\documentclass[
$if(fontsize)$
  $fontsize$,
$endif$
$for(classoption)$
  $classoption$$sep$,
$endfor$
]{$documentclass$}

Then I’ll hard code the documentclass

\documentclass[
$if(fontsize)$
  $fontsize$,
$endif$
$for(classoption)$
  $classoption$$sep$,
$endfor$
]{pnas-new}

At this point, I’ll reopen the template.qmd file and add to the YAML the relevant options for what we were just looking at.

fontsize: "9pt"
classoption:
 - twocolumn
 - twoside

That’s it for that file. Now we just have to let the _extension.yml file know we have it, like we did for the class files above.

Look for the lines we edited before and below it we’ll add a template-partials section to indicate that we have this file.

    pdf:
      include-in-header: header.tex
      format-resources:
        - pnas-new.cls
        - pnas-new.bst
        - pnasresearcharticle.sty
        - jabbrv.sty
        - jabbrv-ltwa-all.ldf
        - jabbrv-ltwa-en.ldf
      template-partials:
        - "partials/doc-class.tex"
title.tex

Now, the title block definitely needs work for this example and probably for the majority of cases. The title partial will tell the document how to create the title, identify the authors, and identify anything else important that needs to be specified at the start of the document. For the PNAS template, we need to tell it how to:

  • Specify the \templatetype{}
  • Call \title{}
  • Identify the authors (this one is the hardest!)
  • Call \leadauthor{}
  • Call \significancestatement{}
  • Call \authorcontributions{}
  • Call \authordeclaration{}
  • Identify equal authors
  • Identify corresponding authors
  • Specify the keywords

For the template type, we want to use an if-else statement. This lets us specify the default option for the case where no type was set. So, since there is an else, we can put it all directly inside the call to \templatetype{}. This will look something like:

\templatetype{
$if(template_type)$
$template_type$
$else$
pnasresearcharticle
$endif$}

where $else$ is how we specify the else part of the if-else.

The next part of this is going to be familiar. If there’s a title, we want to write the title.

$if(title)$
\title{$title$}
$endif$

If we hold onto the author pieces for a second, we can repeat that pattern for each of \leadauthor{},\significancestatement{},\authorcontributions{}, and \authordeclaration{}.

$if(runningauthor)$
\leadauthor{$runningauthor$}
$endif$

$if(significance)$
\significancestatement{$significance$}
$endif$

$if(author_contributions)$
\authorcontributions{$author_contributions$}
$endif$

$if(conflict_of_interest)$
\authordeclaration{$conflict_of_interest$}
$endif$

Now back to the authors and affiliations. This is often the hardest part of making a Quarto template for journals. As a reminder, we want the output to look like:

\author[a,c,1]{Author One}
\author[b,1,2]{Author Two}
\author[a]{Author Three}

\affil[a]{Affiliation One}
\affil[b]{Affiliation Two}
\affil[c]{Affiliation Three}

Let’s break apart what we need for Author One.

  • A name
  • Affiliation with a
    • Need to make the \affil for a
  • Affiliation with b
    • Need to make that \affil for b too
  • a 1 to indicate joint first authorship
    • We’ll fill that part out in a minute.

If we look at the normalized schema for author first, we can figure out how to access it. The relevant part of the YAML looks like:

author:
  - id: string
    number: number
    name:
      given: string
      family: string
      literal: string

How do we get the value? We can build it like so

  • it’s part of the author: $author.
    • it’s part of the name: name.
      • we want the full thing and that’s it literal$

So going down the little tree, we get that it is \(author.name.literal\). If we needed something like the first name, it would be \(author.name.given\).

So, we can throw that into:

\author[...]{$author.name.literal$}

leaving the ... for this next step.

We need to extract the IDs for the affiliations for each person, so we’ll need to iterate. To iterate over the affiliations of each author, we can use the by-author iterator. This will let us loop over each author and grab the relevant values.

If we just needed the authors, then this would look like:

$for(by-author)$
\author{$by-author.name.literal$}
$endfor$

But, we need to fill in the [...] from above. To start, let’s do the affiliation ids. If they have affiliations, we want to loop over them.

$for(by-author)$
\author[
$if(by-author.affiliations)$
$for(by-author.affiliations)$
$it.id$
$sep$,
$endfor$
$endif$
]{$by-author.name.literal$}
$endfor$

As you can see, I’ve introduced the special keyword it. It’s essentially a placeholder for the current iterator. Here, that’s each entry of by-author.affiliations and we are accessing the .id from them. We’re separating them with ,s so that they evaluate nicely.

The spacing that this outputs is a little funny, so we can collapse it to be

$for(by-author)$
\author[$if(by-author.affiliations)$$for(by-author.affiliations)$$it.id$$sep$,$endfor$$endif$%
]{$by-author.name.literal$}
$endfor$

We can use the % just as in LaTeX for the normal end of line behavior.

Now this all works beautifully for the affiliations, but we also need to think about the \(1\) and \(2\). \(1\) means equal first authorship if there are multiple first authors. \(2\) means corresponding author assuming that there are multiple first authors. So, \(1\) isn’t always there but \(2\) should be. Now, we can back out that if there are multiple first authors, then the author listed first will be a first author.

This is good news! This means we can check if there is an equal contribution note. We can do this with our good friend LaTeX, by defining a new command for this. If there’s an equal contributor, it’ll always be 1. We can make the corresponding variable vary based on the equal contributor variable.

\newcommand{\equalcont}{1}

$if(equal-contributor)$
\newcommand{\correspond}{2}
$else$
\newcommand{\correspond}{1}
$endif$

We can then use that in our templating. If an author is listed as an equal author, we want to add a ,\equalcont after the affiliations. So, we can do just that by adding in another line.

$for(by-author)$
\author[$if(by-author.affiliations)$$for(by-author.affiliations)$$it.id$$sep$,$endfor$$endif$%
$if(by-author.attributes.equal-contributor)$,\equalcont$endif$%
]{$by-author.name.literal$}
$endfor$

In the same way, we can add information about corresponding authors. If an author is listed as the corresponding author, we want to add a ,\correspond after the affiliations.

$for(by-author)$
\author[$if(by-author.affiliations)$$for(by-author.affiliations)$$it.id$$sep$,$endfor$$endif$%
$if(by-author.attributes.equal-contributor)$,\equalcont$endif$%
$if(by-author.attributes.corresponding)$,\correspond$endif$%
]{$by-author.name.literal$}
$endfor$

Now, we can turn to setting up the affiliation lines, with \affil. As with authors, there is a by-affiliation keyword that will let us iterate over the affiliations. For each affilition, we want something of the form:

\affil[a]{Affiliation One}

At the most basic level, if it was just a name (like “Harvard University”), we could use a loop and the it syntax again to make something like this:

$for(by-affiliation)$
\affil[$it.id$]{$it.name$}
$endfor$

We need it to do a teeny bit more though. We want something instead that uses all of the relevant pieces of the YAML to say:

Harvard University, Department of Government, Cambridge, MA 02138

The components of this are then:

name, department, city, state, postal-code

For each affiliation, we would want something like:

$if(it.name)$$it.name$$endif$ 
$if(it.department)$, $it.department$$endif$ 
$if(it.city)$, $it.city$$endif$ 
$if(it.state)$, $it.state$$endif$ 
$if(it.postal-code)$, $it.postal-code$$endif$ 
$if(it.country)$, $it.country$$endif$

Note that we’re separating them with , at the start of each additional argument. This avoids weird spacing in the output. And we should probably add in country at the end, for if not all authors work in the US.

$if(it.country)$, $it.country$$endif$

We can put this all in its own file, called _affiliation.tex in the partials/ directory. We don’t have to, but it seems to be common practice in existing templates. Then, we can call this chunk using $_affiliation.tex()$. The benefit of doing that is it keeps the template cleaner and easier to debug later.

$for(by-affiliation)$
\affil[$it.id$]{$_affiliation.tex()$}
$endfor$

Don’t forget to add the partials/_affiliation.tex line to the _extension file. That will now have a template-partials chunk like so:

      template-partials:
        - "partials/doc-class.tex"
        - "partials/_affiliation.tex"
        - "partials/title.tex"

Now, a minute ago, we did the hard work for the equal contributors, so we can add in some code to indicate the equal authors too in the template:

$if(equal-contributor)$
\equalauthors{\textsuperscript{\equalcont} $equal-contributor$}
$endif$

We add the superscript because we had just a raw number above.

Now, we can do something similar for the corresponding author. This time we’ll iterate over the authors, again with by-author to find the corresponding one. If they’re the corresponding author, we’ll fill in the ... below:

$for(by-author)$
$if(by-author.attributes.corresponding)$
\correspondingauthor{...}
$endif$
$endfor$

Just as with the equal contributors, we want to start it with the identifier, \textsuperscript{\correspond}. Then we need the PNAS template text: “To whom correspondence should be addressed. E-mail:”. And last, we can get the email from the author with $by-author.email$.

Put together, that looks like:

$for(by-author)$
$if(by-author.attributes.corresponding)$
\correspondingauthor{\textsuperscript{\correspond}To whom correspondence should be addressed. E-mail: $by-author.email$}
$endif$
$endfor$

Finally, we can build out the keywords. Since there can be multiple, we’ll use a for loop with a separator, like in Section 3.2.1.1. We want to iterate over each one and print them separated by a pipe, |.

$if(keywords)$
\keywords{$for(keywords)$$keywords$$sep$ | $endfor$}
$endif$

All together, the file looks something like:

\templatetype{$if(template_type)$$template_type$$else$pnasresearcharticle$endif$}

$if(title)$
\title{$title$}
$endif$

\newcommand{\equalcont}{1}

$if(equal-contributor)$
\newcommand{\correspond}{2}
$else$
\newcommand{\correspond}{1}
$endif$

$for(by-author)$
\author[$if(by-author.affiliations)$$for(by-author.affiliations)$$it.id$$sep$,$endfor$$endif$%
$if(by-author.attributes.equal-contributor)$,\equalcont$endif$%
$if(by-author.attributes.corresponding)$,\correspond$endif$%
]{$by-author.name.literal$}
$endfor$

$for(by-affiliation)$
\affil[$it.id$]{$_affiliation.tex()$}
$endfor$

$if(runningauthor)$
\leadauthor{$runningauthor$}
$endif$

$if(significance)$
\significancestatement{$significance$}
$endif$

$if(author_contributions)$
\authorcontributions{$author_contributions$}
$endif$

$if(conflict_of_interest)$
\authordeclaration{$conflict_of_interest$}
$endif$

$if(equal-contributor)$
\equalauthors{\textsuperscript{\equalcont} $equal-contributor$}
$endif$

$for(by-author)$
$if(by-author.attributes.corresponding)$
\correspondingauthor{\textsuperscript{\correspond}To whom correspondence should be addressed. E-mail: $by-author.email$}
$endif$
$endfor$

$if(keywords)$
\keywords{$for(keywords)$$keywords$$sep$ | $endfor$}
$endif$

Note that it will eventually be processeed from top to bottom, so we have to define the LaTeX commands before we use them.

before-body.tex

Okay, with the hardest part done, we can do this one without too much crazy stuff. This partial is all the stuff that comes after \begin{document} but before the writing. So, that normally includes making the title and the abstract. As you’ll see in a minute, there’s also some generic LaTeX from the template that we want here.

First, if there’s a title we have to tell it to make it. This looks like much of how we built out the title section.

$if(title)$
\maketitle
$endif$

Next, if there’s an abstract, we need to make that. We also need it to be in an abstract environment, so we can toss the whole thing into one if statement. For what it’s worth, this chunk goes in pretty much every template. I just copy the same chunk from old Quarto template to new Quarto template whenever I need it.

$if(abstract)$
\begin{abstract}
$abstract$
\end{abstract}
$endif$

And really both of these two things are handled by the default partial. I would have preferred to not change it, but we need some defaults to be set here that we see in the template itself. We can just straight up copy those into it. No edits necessary. If you go the route to allow setting the doi, perhaps with a YAML doi:, this is where you would insert that.

\dates{This manuscript was compiled on \today}
\doi{\url{www.pnas.org/cgi/doi/10.1073/pnas.XXXXXXXXXX}}

\thispagestyle{firststyle}
\ifthenelse{\boolean{shortarticle}}{\ifthenelse{\boolean{singlecolumn}}{\abscontentformatted}{\abscontent}}{}

All together, the before-body partial looks something like:

$if(title)$
\maketitle
$endif$

$if(abstract)$
\begin{abstract}
$abstract$
\end{abstract}
$endif$

\dates{This manuscript was compiled on \today}
\doi{\url{www.pnas.org/cgi/doi/10.1073/pnas.XXXXXXXXXX}}

\thispagestyle{firststyle}
\ifthenelse{\boolean{shortarticle}}{\ifthenelse{\boolean{singlecolumn}}{\abscontentformatted}{\abscontent}}{}

This is about the “simplest” partial to make, once you’re comfortable with partials, since it is so close to the default. If it weren’t for setting the first page style, we could have omitted it. Don’t forget to add it to _extension.yml.

before-bib.tex

In the PNAS template, just before the bibliography, we need to include the acknowledgements. We also need to make a call to a function, \showacknow{} to make them appear.

The first part is the same as our usual pattern. Check if there are acknowledgements, and if there are, set them.

$if(acknowledgements)$
\acknow{$acknowledgements$}

\showacknow{} % Display the acknowledgments section
$endif$

Within the check, we’ll add the quick call to make sure they appear.

As before, we to add it to _extension.yml. But now we’re done with most of the work!

Editing template.qmd (Part 2: The Body)

Okay, now we can test and run with the file. To do so, we want to add some text back from the other template that instructs the user how to fill out the template. I’ll start by copying in big chunks from the template and then converting things from LaTeX to the friendlier Quarto (or sometimes R) syntax.

For example, if the LaTeX template has:

\subsection*{Author Affiliations}

I’ll make that into:

## Author Affiliations {.unnumbered}

We can replace things like sections or links with some find+replace regex in RStudio:

  • Replace (\\subsection\*{)(.+?)(}) with ## \2 {.unnumbered} to fix subsection titles.
  • Replace (\\subsubsection\*{)(.+?)(}) with ### \2 {.unnumbered} to fix subsubsection titles.
  • Replace (\\href{)(.+?)(})({)(.+?)(}) with [\5](\2) to move links from LaTeX to Quarto.
  • Replace (\\ref{fig:)(.+?)(}) with @fig-\2 to move cross references to Quarto syntax.
  • Replace (\\verb\|)(.+?)(\|) with \\2`` to move verbatim environments to Quarto ones.

As I was doing this, I got a really weird error.

compilation failed- error
LaTeX Error: Not in outer par mode.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.206 \end{document}
                     

see template.log for more information.

The obvious thing was that I probably had a mismatch in } or { somewhere. After a few minutes, that clearly wasn’t the case. They all seemed to match and match correctly.

At this point, I looked at the template again and thought about the first page special formatting. The first page has a different column width, so it’s a little special. I added some more paragraphs from the LaTeX template and it rendered fine. Playing around and if there was anything short of a page, it would fail. Otherwise, it worked beautifully. As such, I added a warning to the template in the text, so any overzealous compilers would have a clue what broke.

A word of warning: This template will fail with a "Not in outer par mode" warning if you try to compile it with less than one page of text.
The template relies on having a full first page which is styled separately.
If you see a warning to the tune of "LaTeX Error: Not in outer par mode." or referencing `\end{document}`, try writing more or adding filler text and recompiling.

Next, I’ll replace figures. For the most basic figures, I’ll just use raw Quarto figure syntax.

For example:

\begin{figure}%[tbhp]
\centering
\includegraphics[width=.8\linewidth]{figs/frog.pdf}
\caption{Placeholder image of a frog with a long example legend to show justification setting.}
\label{fig:frog}
\end{figure}

can be pretty well replicated with the shorter:

![Placeholder image of a frog with a long example legend to show justification setting.](figs/frog.pdf){#fig-frog}

Additional settings can be set within an R chunk (or other code chunk). I find it very helpful to mention the fig-env argument, such as below:

In Quarto, we can do these by setting the fig-env command to figure* or SCfigure*

#| label: fig-side
#| fig-cap: "This legend would be placed at the side of the figure, rather than below it."
#| fig-env: "SCfigure*"
#| echo: false
# tell it the options as comments with a | and a space, as above.
# set echo: false to avoid printing this text
knitr::include_graphics('figs/frog.pdf')

Two column journals need the figure* (often called a “star figure”, “figure star”, or “star” environment) for page wide columns. PNAS also has a side-caption version, which is included in the template as the above example. This gives a clear demo of a simpler approach to including figures, at least than with LaTeX.

As for tables, I tend to leave them as-is in LaTeX. Many table environments need a little something else that doesn’t seem to translate super well into Markdown syntax. Of course, you could translate them into Markdown, especially if the LaTeX doesn’t need special options.

Finally, I like to include anything about the bibliography in a References section, like below. I include the special \bibsplit command that needs to be set manually at the end with a comment explaining how to use it.

# References
\bibsplit[2]
<!-- Use \bibsplit to split the references from the body of the text. Value "[2]" represents the number of reference in the left column (Note: Please avoid single column figures & tables on this page.) -->

:::{#refs}
:::

This includes the special reference div (the ::: things) to help make it easier to understand where those will be printed. Details on that div are available in the Quarto documentation.

Note

Don’t forget to update the bibliography.bib default file to include any example references you use!

Cleaning up

Revising the README

With the Quarto and LaTeX stuff pretty much done, we want to include information for future users.

First, we’ll fill out the Options section. In general, this should describe anything that isn’t default in the YAML or that should generally be set.

For example, I want to explain a few things:

  • classoption defaults
  • setting a corresponding author
  • affiliation IDs should be letters

Words are useful here to refer

For the PNAS template, I included the following:

The default setting for class option generates a two column layout with:

classoption:
 - twocolumn
 - twoside

To set a corresponding author, ensure that the attribute “corresponding” is true and that they have an email listed. For proper formatting, each affiliation should be given a letter id (like a, b, …, z). This template is designed for template_type: pnasresearcharticle (the default). It can also take options pnasmathematics or pnasinvited, but these are not formally supported, as they have official alternative formats available on Overleaf.

Now, we can run the png code from before.

pdftools::pdf_convert('template.pdf', pages = 1)

will generate a file template_1.png.

To include that in the readme, we can use Markdown, like:

![[template.qmd](template.qmd)](template_1.png)

That gives people enough information to get started with your template.

Adding a .quartoignore

Now, one last thing we want to do is make sure that people using the template won’t install extra files. Things like template_1.png from the last section are useful in the repo, but not to the end user. We can add a file .quartoignore in the root directory that functions like a .gitignore file.

Mine looks like:

*.pdf
*.png
*.rproj
*.Rproj
!figs/*.png

This ignores all pdfs, pngs, and R project files. It then un-ignores the figs/ folder where useful example files for the template live. It’s important to include those in the template because you want the file that gets downloaded to run when someone runs

quarto use template christopherkenny/pnas

But, they can be deleted once people have made sure the template works. As such, they don’t need to be included in the _extension.yml file, but shouldn’t be .quartoignored.

Deleting unused files

It’s probably over. There may be other changes, but it’s probably over. But it’s not an official template, so no one will contact you when they update it. But, one day, you might notice that it is different or someone will comment on GitHub. But for now, you’re free! Time to make another template for that other journal you were thinking about submitting to.

More importantly, at this point, you should delete any files or code that you didn’t use. For this template, that means removing styles.css and pnas.lua. Also, remove them from the _extension.yml file. That is, delete:

      filters:
        - pnas.lua

and

    html:
      css: styles.css

Finishing up

Last things last.

If your template now works, then it’s time to make it public. A seemingly ridiculous portion of research time is spent recreating resources that surely someone else has somewhere. A semi-functional template is better than starting from scratch.

Add a topic to the GitHub repo for quarto-template and anything else relevant! Congrats, you have a template. Share it on Twitter or whatever is still useful. If it doesn’t work for someone, they’ll email you or open an issue. As a wise person once said > all (feedback) is good (feedback)

Even if it didn’t work for someone, a little information about why it didn’t work can help you a ton for when you have the same issue some day.

Resources

The template created within this post is available at christopherkenny/pnas.

Citation

BibTeX citation:
@online{t. kenny2023,
  author = {T. Kenny, Christopher},
  title = {Creating {Quarto} {Journal} {Article} {Templates}},
  date = {2023-07-01},
  url = {https://christopherkenny/posts/2023-07-01-creating-quarto-journal-articles},
  langid = {en}
}
For attribution, please cite this work as:
T. Kenny, Christopher. 2023. “Creating Quarto Journal Article Templates.” July 1, 2023. https://christopherkenny/posts/2023-07-01-creating-quarto-journal-articles.