diff --git a/AUTHORS.md b/AUTHORS.md
index e11e98885..4e762fd08 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -69,6 +69,7 @@
- Cezary Drożak
- Chandrahas77
- Charanjit Singh
+- Charles Tapley Hoyt
- Charlotte Koch
- Chris Black
- Christian Conkle
@@ -197,6 +198,7 @@
- Jeroen de Haas
- Jerry Sky
- Jesse Rosenthal
+- Jez Cope
- Joe Hermaszewski
- Joe Hillenbrand
- John KetzerX
diff --git a/data/templates/article.jats_publishing b/data/templates/article.jats_publishing
index 981f86762..d402b8bbd 100644
--- a/data/templates/article.jats_publishing
+++ b/data/templates/article.jats_publishing
@@ -109,6 +109,17 @@ $elseif(author.name)$
$else$
$author$
$endif$
+$for(author.roles)$
+$if(it.credit)$
+$if(it.name)$$it.name$$else$$it.credit-name$$endif$
+$elseif(it.name)$
+$it.name$
+$endif$
+$endfor$
$if(author.email)$
$author.email$
$endif$
diff --git a/doc/jats.md b/doc/jats.md
index 4ac54f25b..a723ccce5 100644
--- a/doc/jats.md
+++ b/doc/jats.md
@@ -61,6 +61,85 @@ Metadata Values
set it used, as affiliation links are not allowed in that
schema.
+ `roles`
+ : a list of dictionaries describing the author's role(s).
+ Each role is added as an [``] element to
+ the author's [``] element. The following examples
+ illustrate:
+
+ An ad-hoc role:
+
+ ```yaml
+ roles:
+ - name: Dolphin Catcher
+ ```
+
+ A role specified with CRediT.
+
+ ```yaml
+ roles:
+ - credit: writing-review-editing
+ ```
+
+ The `credit-name` is automatically looked up from
+ the CRediT taxonomy, but you can also specify it
+ yourself:
+
+ ```yaml
+ roles:
+ - credit: writing-review-editing
+ credit-name: Writing – review & editing
+ ```
+
+ A role specified with CRediT, including an
+ optional degree of contribution. Note that
+ specifying the degree only is allowed when
+ using CRediT roles and not ad-hoc roles.
+
+ ```yaml
+ roles:
+ - credit: writing-review-editing
+ degree: Lead
+ ```
+
+ A role specified with CRediT with a label override,
+ useful for internationalization:
+
+ ```yaml
+ roles:
+ - credit: writing-review-editing
+ name: Escrita – revisão e edição
+ ```
+
+ The value for `credit` and `credit-name`
+ must be from one of the 14 terms from the
+ Contribution Role Taxonomy (CRediT):
+
+ | `credit` | `credit-name` |
+ |--------------------------|----------------------------|
+ | `conceptualization` | Conceptualization |
+ | `data-curation` | Data curation |
+ | `formal-analysis` | Formal analysis |
+ | `funding-acquisition` | Funding acquisition |
+ | `investigation` | Investigation |
+ | `methodology` | Methodology |
+ | `project-administration` | Project administration |
+ | `resources` | Resources |
+ | `software` | Software |
+ | `supervision` | Supervision |
+ | `validation` | Validation |
+ | `visualization` | Visualization |
+ | `writing-original-draft` | Writing – original draft |
+ | `writing-review-editing` | Writing – review & editing |
+
+ JATS suggests in [``] to use one of
+ the following three values when specifying the degree of
+ contribution:
+
+ 1. `Lead`
+ 2. `Equal`
+ 3. `Supporting`
+
`equal-contrib`
: boolean attribute used to mark authors who contributed
equally to the work. The
@@ -483,3 +562,5 @@ Required metadata values:
[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-wrap.html
[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution.html
[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/pub-date.html
+[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/role.html
+[``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/degree-contribution.html
diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs
index e09738908..dc288c6c0 100644
--- a/src/Text/Pandoc/Writers/JATS.hs
+++ b/src/Text/Pandoc/Writers/JATS.hs
@@ -27,7 +27,7 @@ import Control.Monad.Reader
import Control.Monad.State
import Data.Generics (everywhere, mkT)
import qualified Data.Map as M
-import Data.Maybe (fromMaybe, listToMaybe)
+import Data.Maybe (fromMaybe, listToMaybe, isNothing)
import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime)
import qualified Data.Text as T
import Data.Text (Text)
@@ -43,7 +43,7 @@ import Text.DocLayout
import Text.Pandoc.Shared
import Text.Pandoc.URI
import Text.Pandoc.Templates (renderTemplate)
-import Text.DocTemplates (Context(..), Val(..))
+import Text.DocTemplates (Context(..), Val(..), toVal)
import Text.Pandoc.Writers.JATS.References (referencesToJATS)
import Text.Pandoc.Writers.JATS.Table (tableToJATS)
import Text.Pandoc.Writers.JATS.Types
@@ -54,6 +54,54 @@ import Text.TeXMath
import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
import qualified Text.XML.Light as Xml
+-- | Default human-readable names for roles in the Contributor Role
+-- Taxonomy (CRediT). This is useful for generating JATS that annotate
+-- contributor roles
+creditNames :: M.Map Text Text
+creditNames = M.fromList [
+ ("conceptualization", "Conceptualization"),
+ ("data-curation", "Data curation"),
+ ("formal-analysis", "Formal analysis"),
+ ("funding-acquisition", "Funding acquisition"),
+ ("investigation", "Investigation"),
+ ("methodology", "Methodology"),
+ ("project-administration", "Project administration"),
+ ("resources", "Resources"),
+ ("software", "Software"),
+ ("supervision", "Supervision"),
+ ("validation", "Validation"),
+ ("visualization", "Visualization"),
+ ("writing-original-draft", "Writing – original draft"),
+ ("writing-review-editing", "Writing – review & editing")]
+
+-- | Ensure every role with a `credit` key also has a `credit-name`,
+-- using a default value if necessary
+addCreditNames :: Context Text -> Context Text
+addCreditNames context =
+ case getField "author" context of
+ -- If there is an "authors" key, then we replace the existing value
+ -- with one we mutate by running the addCreditNamesToAuthor helper
+ -- function on each
+ Just (ListVal authors) ->
+ resetField "author" (map addCreditNamesToAuthor authors) context
+ -- If there is no "authors" key in the context, then we don't have to do
+ -- anything, and just return the context as is
+ _ -> context
+ where
+ addCreditNamesToAuthor :: Val Text -> Val Text
+ addCreditNamesToAuthor val = fromMaybe val $ do
+ MapVal authorCtx <- pure val
+ ListVal roles <- getField "roles" authorCtx
+ return $ toVal $ resetField "roles" (map addCreditNameToRole roles) authorCtx
+
+ addCreditNameToRole :: Val Text -> Val Text
+ addCreditNameToRole val = fromMaybe val $ do
+ MapVal roleCtx <- pure val
+ guard $ isNothing (getField "credit-name" roleCtx :: Maybe (Val Text))
+ creditId <- getField "credit" roleCtx
+ creditName <- M.lookup creditId creditNames
+ return $ toVal $ resetField "credit-name" creditName roleCtx
+
-- | Convert a @'Pandoc'@ document to JATS (Archiving and Interchange
-- Tag Set.)
writeJatsArchiving :: PandocMonad m => WriterOptions -> Pandoc -> m Text
@@ -159,6 +207,7 @@ docToJATS opts (Pandoc meta blocks') = do
(lookupMetaInlines "title" meta)
let context = defField "body" main
$ defField "back" back
+ $ addCreditNames
$ resetField "title" title'
$ resetField "date" date
$ defField "mathml" (case writerHTMLMathMethod opts of
diff --git a/test/command/10152.md b/test/command/10152.md
new file mode 100644
index 000000000..44c5df625
--- /dev/null
+++ b/test/command/10152.md
@@ -0,0 +1,446 @@
+# CRediT Roles
+
+This document contains tests and examples for enabling
+export of roles to JATS. It was added for
+[Issue #10152](https://github.com/jgm/pandoc/issues/10152)
+and corresponding [Pull Request #10153](https://github.com/jgm/pandoc/pull/10153).
+
+In the first example, we show a fully qualified CRediT role.
+An explicit name isn't given, so the CRediT name is used.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - credit: software
+ credit-name: Software
+ degree: Lead
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Software
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In the second example, we show a fully qualified CRediT role.
+An explicit name is given in a different language.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - credit: software
+ credit-name: Software
+ degree: Lead
+ name: Programas
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Programas
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In this example, we show a partially qualified CRediT role
+that does not have a `degree`:
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - credit: software
+ credit-name: Software
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Software
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In this example, we show a more stripped-down data that requires automatic lookup of the
+`credit-name`.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - credit: software
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Software
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In this example, we test the correct XML encoding of
+the CRediT role [Writing – review & editing](https://credit.niso.org/contributor-roles/writing-review-editing/),
+which annoyingly contains an ampersand in its label.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - credit: writing-review-editing
+ credit-name: Writing – review & editing
+ degree: Lead
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Writing – review & editing
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In this example, we show a role that isn't qualified with CRediT.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - name: Dolphin Catcher
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+Dolphin Catcher
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+In this example, we show a role that neither has a CRediT identifer,
+nor a name, so it's ignored.
+
+```
+% pandoc -s -t jats
+---
+title: CRediT Test
+author:
+ - name: Max Mustermann
+ affiliation: [ 1 ]
+ roles:
+ - irrelevant-key: Dolphin Catcher
+affiliation:
+- id: 1
+ name: Silverlight University
+---
+^D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CRediT Test
+
+
+
+Max Mustermann
+
+
+
+
+Silverlight University
+
+
+
+
+
+
+
+
+
+
+
+
+
+```