add _extensions

This commit is contained in:
2026-05-21 13:37:53 +08:00
parent 6a9a5fc90e
commit 61bd0bea2f
252 changed files with 33972 additions and 1 deletions
@@ -0,0 +1,16 @@
title: Authors and affiliation formatting for quarto
authors:
- name: Ming Su
email: mingsu@rcees.ac.cn
orcid: 0000-0001-9821-1268
url: https://drwater.net/team/ming-su/
- name: Lorenz A. Kapsner
orcid: 0000-0003-1866-860X
- name: Albert Krewinkel
orcid: 0000-0002-9455-0796
- name: Robert Winkler
version: 0.3.2
quarto-required: ">=1.3.0"
contributes:
filters:
- authoraffil.lua
@@ -0,0 +1,16 @@
title: Authors and affiliation formatting for quarto
authors:
- name: Ming Su
email: mingsu@rcees.ac.cn
orcid: 0000-0001-9821-1268
url: https://drwater.net/team/ming-su/
- name: Lorenz A. Kapsner
orcid: 0000-0003-1866-860X
- name: Albert Krewinkel
orcid: 0000-0002-9455-0796
- name: Robert Winkler
version: 0.3.2
quarto-required: ">=1.3.0"
contributes:
filters:
- authoraffil.lua
@@ -0,0 +1,75 @@
--[[
authors-block affiliations block extension for quarto
Copyright (c) 2023 Lorenz A. Kapsner
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local from_utils = require("utils")
local normalize_affiliations = from_utils.normalize_affiliations
local normalize_authors = from_utils.normalize_authors
local from_authors = require("from_author_info_blocks")
local default_marks = from_authors.default_marks
local create_equal_contributors_block = from_authors.create_equal_contributors_block
local create_affiliations_blocks = from_authors.create_affiliations_blocks
local create_correspondence_blocks = from_authors.create_correspondence_blocks
local is_corresponding_author = from_authors.is_corresponding_author
local author_inline_generator = from_authors.author_inline_generator
local create_authors_inlines = from_authors.create_authors_inlines
function Pandoc(doc)
local meta = doc.meta
local body = List:new({})
local mark = function(mark_name)
return default_marks[mark_name]
end
-- Process CRediT roles
if meta.authors then
local credit_roles = List:new({})
for i, author in ipairs(meta.authors) do
if author.role then
local roles = List:new({})
for role, level in pairs(author.role) do
roles:insert(pandoc.Str(role .. ": " .. stringify(level)))
end
if #roles > 0 then
local author_name = stringify(author.name)
credit_roles:insert(pandoc.Para({
pandoc.Str(author_name .. ": "),
pandoc.Str(table.concat(roles:map(stringify), ", ")),
}))
end
end
end
if #credit_roles > 0 then
body:insert(pandoc.Header(2, pandoc.Str("Author Contributions")))
body:extend(credit_roles)
end
end
body:extend(create_equal_contributors_block(meta.authors, mark) or {})
body:extend(create_affiliations_blocks(meta.affiliations, meta) or {})
body:extend(create_correspondence_blocks(meta.authors, mark) or {})
body:extend(doc.blocks)
for _i, author in ipairs(meta.authors) do
author.test = is_corresponding_author(author)
end
meta.affiliations = normalize_affiliations(meta.affiliations or {})
meta.author = meta.authors:map(normalize_authors(meta.affiliations))
meta.author = pandoc.MetaInlines(create_authors_inlines(meta.author, mark, meta))
meta.affiliations = nil
return pandoc.Pandoc(body, meta)
end
@@ -0,0 +1,297 @@
--[[
affiliation-blocks generate title components
Copyright © 20172021 Albert Krewinkel
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local from_utils = require("utils")
local has_key = from_utils.has_key
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local M = {}
local default_marks = {
corresponding_author = FORMAT == "latex" and { pandoc.RawInline("latex", "*") } or { pandoc.Str("*") },
equal_contributor = FORMAT == "latex" and { pandoc.RawInline("latex", "\\#") } or { pandoc.Str("#") },
}
local function get_orcid_mark(orcid_value)
if not orcid_value then
return {}
end
local orcid_str
if type(orcid_value) == "string" then
orcid_str = orcid_value
elseif type(orcid_value) == "table" then
if orcid_value.text then
orcid_str = orcid_value.text
elseif orcid_value[1] and orcid_value[1].text then
orcid_str = orcid_value[1].text
else
return {}
end
else
return {}
end
orcid_str = orcid_str:gsub("[%-%s]", "")
if FORMAT == "latex" then
return { pandoc.RawInline("latex", "\\orcidlink{" .. orcid_str .. "}") }
elseif FORMAT:match("docx") then
local orcid_url = "https://orcid.org/" .. orcid_str
return {
pandoc.Str(" "),
pandoc.Link("ID", orcid_url),
}
else
local orcid_url = "https://orcid.org/" .. orcid_str
return { pandoc.Link(pandoc.Str(""), orcid_url, "", { class = "orcid" }) }
end
end
M.default_marks = default_marks
local function is_equal_contributor(author)
if has_key(author, "attributes") then
return author.attributes["equal-contributor"]
end
return nil
end
local function create_equal_contributors_block(authors, mark)
local has_equal_contribs = List:new(authors):find_if(is_equal_contributor)
if not has_equal_contribs then
return nil
end
local contributors = {
pandoc.Superscript(mark("equal_contributor")),
pandoc.Space(),
pandoc.Str("These authors contributed equally to this work."),
}
return List:new({ pandoc.Para(contributors) })
end
M.create_equal_contributors_block = create_equal_contributors_block
local function intercalate(lists, elem)
local result = List:new({})
for i = 1, (#lists - 1) do
result:extend(lists[i])
result:extend(elem)
end
if #lists > 0 then
result:extend(lists[#lists])
end
return result
end
local function is_corresponding_author(author)
if has_key(author, "attributes") then
if author.attributes["corresponding"] then
return author.email
end
end
return nil
end
M.is_corresponding_author = is_corresponding_author
local function create_correspondence_blocks(authors, mark)
local corresponding_authors = List:new({})
for _, author in ipairs(authors) do
if is_corresponding_author(author) then
local mailto = "mailto:" .. stringify(author.email)
local author_with_mail = List:new(
author.name.literal
.. List:new({ pandoc.Space(), pandoc.Str("(") })
.. author.email
.. List:new({ pandoc.Str(")") })
)
local link = pandoc.Link(author_with_mail, mailto)
table.insert(corresponding_authors, { link })
end
end
if #corresponding_authors == 0 then
return nil
end
local correspondence = List:new({
pandoc.Superscript(mark("corresponding_author")),
pandoc.Space(),
pandoc.Str("Corresponding to:"),
pandoc.Space(),
})
local sep = List:new({ pandoc.Str(","), pandoc.Space() })
return { pandoc.Para(correspondence .. intercalate(corresponding_authors, sep)) }
end
M.create_correspondence_blocks = create_correspondence_blocks
local function author_inline_generator(get_mark, meta)
return function(author)
local author_marks = List:new({})
if has_key(author, "orcid") then
author_marks[#author_marks + 1] = get_orcid_mark(author.orcid)
end
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
for _, idx in ipairs(author.affiliations) do
local idx_num = tonumber(stringify(idx)) -- Convert MetaString/MetaInlines to number
if not idx_num then
error("Invalid affiliation index: " .. tostring(idx))
end
local idx_str
if affilstyle == "number" then
idx_str = tostring(idx_num)
else
if idx_num > 26 then
error("Too many affiliations: only up to 26 (a-z) are supported")
end
idx_str = string.char(96 + idx_num)
end
author_marks[#author_marks + 1] = { pandoc.Str(idx_str) }
end
if has_key(author, "attributes") then
if author.attributes["equal-contributor"] then
author_marks[#author_marks + 1] = get_mark("equal_contributor")
end
end
if is_corresponding_author(author) then
author_marks[#author_marks + 1] = get_mark("corresponding_author")
end
if FORMAT:match("latex") then
author.name.literal[#author.name.literal + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
return author
else
local res = List.clone(author.name.literal)
res[#res + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
return res
end
end
end
M.author_inline_generator = author_inline_generator
local function create_authors_inlines(authors, mark, meta)
local inlines_generator = author_inline_generator(mark, meta)
local inlines = List:new(authors):map(inlines_generator)
local and_str = List:new({ pandoc.Space(), pandoc.Str("and"), pandoc.Space() })
local last_author = inlines[#inlines]
inlines[#inlines] = nil
local result = intercalate(inlines, { pandoc.Str(","), pandoc.Space() })
if #authors > 1 then
if #authors == 2 then
result:extend(and_str)
else
result:extend(List:new({ pandoc.Str(",") }) .. and_str)
end
end
result:extend(last_author)
return result
end
M.create_authors_inlines = create_authors_inlines
local function create_affiliations_blocks_alphabeta(affiliations, meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
local affil_lines = List:new(affiliations):map(function(affil, i)
if affilstyle == "number" then
num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(tostring(i))),
pandoc.Space(),
})
else
num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(string.char(96 + i))),
pandoc.Space(),
})
end
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
or { pandoc.Str(tostring(affil["postal-code"])) }
local country_inlines = type(affil.country) == "table" and affil.country
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
return num_inlines
:extend(name_inlines)
:extend({ pandoc.Str(", ") })
:extend(city_inlines)
:extend({ pandoc.Space() })
:extend(postcode_inlines)
:extend({ pandoc.Str(", ") })
:extend(country_inlines)
:extend({ pandoc.Str(".") })
end)
local combined_inlines = pandoc.List:new()
for i, line in ipairs(affil_lines) do
combined_inlines:extend(line)
if i < #affil_lines then
combined_inlines:extend({ pandoc.LineBreak() })
end
end
return { pandoc.Para(combined_inlines) }
end
local function create_affiliations_blocks_number(affiliations, meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
local affil_lines = List:new(affiliations):map(function(affil, i)
local num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(tostring(i))),
pandoc.Space(),
})
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
or { pandoc.Str(tostring(affil["postal-code"])) }
local country_inlines = type(affil.country) == "table" and affil.country
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
return num_inlines
:extend(name_inlines)
:extend({ pandoc.Str(", ") })
:extend(city_inlines)
:extend({ pandoc.Space() })
:extend(postcode_inlines)
:extend({ pandoc.Str(", ") })
:extend(country_inlines)
:extend({ pandoc.Str(".") })
end)
local combined_inlines = pandoc.List:new()
for i, line in ipairs(affil_lines) do
combined_inlines:extend(line)
if i < #affil_lines then
combined_inlines:extend({ pandoc.LineBreak() })
end
end
return { pandoc.Para(combined_inlines) }
end
M.create_affiliations_blocks = create_affiliations_blocks_alphabeta
function Meta(meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
M.create_affiliations_blocks = affilstyle == "number" and create_affiliations_blocks_number
or create_affiliations_blocks_alphabeta
if meta.authors then
meta.author = create_authors_inlines(meta.authors, M.default_marks, meta)
end
if meta.affiliations then
meta.institute = M.create_affiliations_blocks(meta.affiliations, meta)
end
if meta.authors then
local equal_contributors = create_equal_contributors_block(meta.authors, M.default_marks)
if equal_contributors then
meta["equal-contributors"] = equal_contributors
end
local correspondence = create_correspondence_blocks(meta.authors, M.default_marks)
if correspondence then
meta.correspondence = correspondence
end
end
return meta
end
return M
@@ -0,0 +1,51 @@
--[[
ScholarlyMeta normalize author/affiliation meta variables
Copyright (c) 2017-2021 Albert Krewinkel, Robert Winkler
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local M = {}
local function has_id(id)
return function(x)
return x.id == id
end
end
local function resolve_institutes(institute, known_institutes)
local unresolved_institutes
if institute == nil then
unresolved_institutes = {}
elseif type(institute) == "string" or type(institute) == "number" then
unresolved_institutes = { institute }
else
unresolved_institutes = institute
end
local result = List:new({})
for i, inst in ipairs(unresolved_institutes) do
local intermed_val = known_institutes:find_if(has_id(stringify(inst)))
if intermed_val then
result[i] = pandoc.MetaString(tostring(intermed_val.index))
else
result[i] = pandoc.MetaString(tostring(inst))
end
end
return result
end
M.resolve_institutes = resolve_institutes
return M
@@ -0,0 +1,49 @@
--[[
authors-block affiliations block extension for quarto
Copyright (c) 2023 Lorenz A. Kapsner
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local from_scholarly = require("from_scholarly_metadata")
local resolve_institutes = from_scholarly.resolve_institutes
local M = {}
local function normalize_affiliations(affiliations)
local affiliations_norm = List:new(affiliations or {}):map(function(affil, i)
affil.index = pandoc.MetaString(tostring(i))
affil.id = pandoc.MetaString(stringify(affil.id or affil.name))
return affil
end)
return affiliations_norm
end
M.normalize_affiliations = normalize_affiliations
local function has_key(set, key)
return set[key] ~= nil
end
M.has_key = has_key
local function normalize_authors(affiliations)
return function(auth)
auth.id = pandoc.MetaString(stringify(auth.name))
auth.affiliations = resolve_institutes(auth.affiliations or {}, affiliations)
return auth
end
end
M.normalize_authors = normalize_authors
return M
@@ -0,0 +1,75 @@
--[[
authors-block affiliations block extension for quarto
Copyright (c) 2023 Lorenz A. Kapsner
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local from_utils = require("utils")
local normalize_affiliations = from_utils.normalize_affiliations
local normalize_authors = from_utils.normalize_authors
local from_authors = require("from_author_info_blocks")
local default_marks = from_authors.default_marks
local create_equal_contributors_block = from_authors.create_equal_contributors_block
local create_affiliations_blocks = from_authors.create_affiliations_blocks
local create_correspondence_blocks = from_authors.create_correspondence_blocks
local is_corresponding_author = from_authors.is_corresponding_author
local author_inline_generator = from_authors.author_inline_generator
local create_authors_inlines = from_authors.create_authors_inlines
function Pandoc(doc)
local meta = doc.meta
local body = List:new({})
local mark = function(mark_name)
return default_marks[mark_name]
end
-- Process CRediT roles
if meta.authors then
local credit_roles = List:new({})
for i, author in ipairs(meta.authors) do
if author.role then
local roles = List:new({})
for role, level in pairs(author.role) do
roles:insert(pandoc.Str(role .. ": " .. stringify(level)))
end
if #roles > 0 then
local author_name = stringify(author.name)
credit_roles:insert(pandoc.Para({
pandoc.Str(author_name .. ": "),
pandoc.Str(table.concat(roles:map(stringify), ", ")),
}))
end
end
end
if #credit_roles > 0 then
body:insert(pandoc.Header(2, pandoc.Str("Author Contributions")))
body:extend(credit_roles)
end
end
body:extend(create_equal_contributors_block(meta.authors, mark) or {})
body:extend(create_affiliations_blocks(meta.affiliations, meta) or {})
body:extend(create_correspondence_blocks(meta.authors, mark) or {})
body:extend(doc.blocks)
for _i, author in ipairs(meta.authors) do
author.test = is_corresponding_author(author)
end
meta.affiliations = normalize_affiliations(meta.affiliations or {})
meta.author = meta.authors:map(normalize_authors(meta.affiliations))
meta.author = pandoc.MetaInlines(create_authors_inlines(meta.author, mark, meta))
meta.affiliations = nil
return pandoc.Pandoc(body, meta)
end
@@ -0,0 +1,297 @@
--[[
affiliation-blocks generate title components
Copyright © 20172021 Albert Krewinkel
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local from_utils = require("utils")
local has_key = from_utils.has_key
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local M = {}
local default_marks = {
corresponding_author = FORMAT == "latex" and { pandoc.RawInline("latex", "*") } or { pandoc.Str("*") },
equal_contributor = FORMAT == "latex" and { pandoc.RawInline("latex", "\\#") } or { pandoc.Str("#") },
}
local function get_orcid_mark(orcid_value)
if not orcid_value then
return {}
end
local orcid_str
if type(orcid_value) == "string" then
orcid_str = orcid_value
elseif type(orcid_value) == "table" then
if orcid_value.text then
orcid_str = orcid_value.text
elseif orcid_value[1] and orcid_value[1].text then
orcid_str = orcid_value[1].text
else
return {}
end
else
return {}
end
orcid_str = orcid_str:gsub("[%-%s]", "")
if FORMAT == "latex" then
return { pandoc.RawInline("latex", "\\orcidlink{" .. orcid_str .. "}") }
elseif FORMAT:match("docx") then
local orcid_url = "https://orcid.org/" .. orcid_str
return {
pandoc.Str(" "),
pandoc.Link("ID", orcid_url),
}
else
local orcid_url = "https://orcid.org/" .. orcid_str
return { pandoc.Link(pandoc.Str(""), orcid_url, "", { class = "orcid" }) }
end
end
M.default_marks = default_marks
local function is_equal_contributor(author)
if has_key(author, "attributes") then
return author.attributes["equal-contributor"]
end
return nil
end
local function create_equal_contributors_block(authors, mark)
local has_equal_contribs = List:new(authors):find_if(is_equal_contributor)
if not has_equal_contribs then
return nil
end
local contributors = {
pandoc.Superscript(mark("equal_contributor")),
pandoc.Space(),
pandoc.Str("These authors contributed equally to this work."),
}
return List:new({ pandoc.Para(contributors) })
end
M.create_equal_contributors_block = create_equal_contributors_block
local function intercalate(lists, elem)
local result = List:new({})
for i = 1, (#lists - 1) do
result:extend(lists[i])
result:extend(elem)
end
if #lists > 0 then
result:extend(lists[#lists])
end
return result
end
local function is_corresponding_author(author)
if has_key(author, "attributes") then
if author.attributes["corresponding"] then
return author.email
end
end
return nil
end
M.is_corresponding_author = is_corresponding_author
local function create_correspondence_blocks(authors, mark)
local corresponding_authors = List:new({})
for _, author in ipairs(authors) do
if is_corresponding_author(author) then
local mailto = "mailto:" .. stringify(author.email)
local author_with_mail = List:new(
author.name.literal
.. List:new({ pandoc.Space(), pandoc.Str("(") })
.. author.email
.. List:new({ pandoc.Str(")") })
)
local link = pandoc.Link(author_with_mail, mailto)
table.insert(corresponding_authors, { link })
end
end
if #corresponding_authors == 0 then
return nil
end
local correspondence = List:new({
pandoc.Superscript(mark("corresponding_author")),
pandoc.Space(),
pandoc.Str("Corresponding to:"),
pandoc.Space(),
})
local sep = List:new({ pandoc.Str(","), pandoc.Space() })
return { pandoc.Para(correspondence .. intercalate(corresponding_authors, sep)) }
end
M.create_correspondence_blocks = create_correspondence_blocks
local function author_inline_generator(get_mark, meta)
return function(author)
local author_marks = List:new({})
if has_key(author, "orcid") then
author_marks[#author_marks + 1] = get_orcid_mark(author.orcid)
end
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
for _, idx in ipairs(author.affiliations) do
local idx_num = tonumber(stringify(idx)) -- Convert MetaString/MetaInlines to number
if not idx_num then
error("Invalid affiliation index: " .. tostring(idx))
end
local idx_str
if affilstyle == "number" then
idx_str = tostring(idx_num)
else
if idx_num > 26 then
error("Too many affiliations: only up to 26 (a-z) are supported")
end
idx_str = string.char(96 + idx_num)
end
author_marks[#author_marks + 1] = { pandoc.Str(idx_str) }
end
if has_key(author, "attributes") then
if author.attributes["equal-contributor"] then
author_marks[#author_marks + 1] = get_mark("equal_contributor")
end
end
if is_corresponding_author(author) then
author_marks[#author_marks + 1] = get_mark("corresponding_author")
end
if FORMAT:match("latex") then
author.name.literal[#author.name.literal + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
return author
else
local res = List.clone(author.name.literal)
res[#res + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
return res
end
end
end
M.author_inline_generator = author_inline_generator
local function create_authors_inlines(authors, mark, meta)
local inlines_generator = author_inline_generator(mark, meta)
local inlines = List:new(authors):map(inlines_generator)
local and_str = List:new({ pandoc.Space(), pandoc.Str("and"), pandoc.Space() })
local last_author = inlines[#inlines]
inlines[#inlines] = nil
local result = intercalate(inlines, { pandoc.Str(","), pandoc.Space() })
if #authors > 1 then
if #authors == 2 then
result:extend(and_str)
else
result:extend(List:new({ pandoc.Str(",") }) .. and_str)
end
end
result:extend(last_author)
return result
end
M.create_authors_inlines = create_authors_inlines
local function create_affiliations_blocks_alphabeta(affiliations, meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
local affil_lines = List:new(affiliations):map(function(affil, i)
if affilstyle == "number" then
num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(tostring(i))),
pandoc.Space(),
})
else
num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(string.char(96 + i))),
pandoc.Space(),
})
end
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
or { pandoc.Str(tostring(affil["postal-code"])) }
local country_inlines = type(affil.country) == "table" and affil.country
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
return num_inlines
:extend(name_inlines)
:extend({ pandoc.Str(", ") })
:extend(city_inlines)
:extend({ pandoc.Space() })
:extend(postcode_inlines)
:extend({ pandoc.Str(", ") })
:extend(country_inlines)
:extend({ pandoc.Str(".") })
end)
local combined_inlines = pandoc.List:new()
for i, line in ipairs(affil_lines) do
combined_inlines:extend(line)
if i < #affil_lines then
combined_inlines:extend({ pandoc.LineBreak() })
end
end
return { pandoc.Para(combined_inlines) }
end
local function create_affiliations_blocks_number(affiliations, meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
local affil_lines = List:new(affiliations):map(function(affil, i)
local num_inlines = pandoc.List:new({
pandoc.Superscript(pandoc.Str(tostring(i))),
pandoc.Space(),
})
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
or { pandoc.Str(tostring(affil["postal-code"])) }
local country_inlines = type(affil.country) == "table" and affil.country
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
return num_inlines
:extend(name_inlines)
:extend({ pandoc.Str(", ") })
:extend(city_inlines)
:extend({ pandoc.Space() })
:extend(postcode_inlines)
:extend({ pandoc.Str(", ") })
:extend(country_inlines)
:extend({ pandoc.Str(".") })
end)
local combined_inlines = pandoc.List:new()
for i, line in ipairs(affil_lines) do
combined_inlines:extend(line)
if i < #affil_lines then
combined_inlines:extend({ pandoc.LineBreak() })
end
end
return { pandoc.Para(combined_inlines) }
end
M.create_affiliations_blocks = create_affiliations_blocks_alphabeta
function Meta(meta)
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
M.create_affiliations_blocks = affilstyle == "number" and create_affiliations_blocks_number
or create_affiliations_blocks_alphabeta
if meta.authors then
meta.author = create_authors_inlines(meta.authors, M.default_marks, meta)
end
if meta.affiliations then
meta.institute = M.create_affiliations_blocks(meta.affiliations, meta)
end
if meta.authors then
local equal_contributors = create_equal_contributors_block(meta.authors, M.default_marks)
if equal_contributors then
meta["equal-contributors"] = equal_contributors
end
local correspondence = create_correspondence_blocks(meta.authors, M.default_marks)
if correspondence then
meta.correspondence = correspondence
end
end
return meta
end
return M
@@ -0,0 +1,51 @@
--[[
ScholarlyMeta normalize author/affiliation meta variables
Copyright (c) 2017-2021 Albert Krewinkel, Robert Winkler
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local M = {}
local function has_id(id)
return function(x)
return x.id == id
end
end
local function resolve_institutes(institute, known_institutes)
local unresolved_institutes
if institute == nil then
unresolved_institutes = {}
elseif type(institute) == "string" or type(institute) == "number" then
unresolved_institutes = { institute }
else
unresolved_institutes = institute
end
local result = List:new({})
for i, inst in ipairs(unresolved_institutes) do
local intermed_val = known_institutes:find_if(has_id(stringify(inst)))
if intermed_val then
result[i] = pandoc.MetaString(tostring(intermed_val.index))
else
result[i] = pandoc.MetaString(tostring(inst))
end
end
return result
end
M.resolve_institutes = resolve_institutes
return M
@@ -0,0 +1,115 @@
---
title: "MANUSCRIPT TITLE"
subtitle: "Supplementary Information"
submitjournal: "JOURNAL"
submitid:
lang: en
date: ""
# bibliography: [BB/Ref.bib, BB/localRef.bib]
affilstyle: alphabeta # number
author:
- name: Xxxxx Yyyy
affiliations:
- ref: kleac
# email: 13586740928@163.com
attributes:
corresponding: false
equal-contributor: true
role:
- data-curation: lead
- methodology: lead
- formalanalysis: lead
- writingoriginal: equal
- writingediting: eqaul
- visualisation: supporting
- name: Ming Su
email: mingsu@rcees.ac.cn
url: https://drwater.net/team/ming-su/
role:
- conceptualization: lead
- methodology: supporting
- formalanalysis: lead
- writingoriginal: equal
- writingediting: equal
- visualisation: lead
- projectadmin: lead
- supervision
affiliations:
- ref: kleac
- ref: ucas
attributes:
corresponding: true
equal-contributor: true
- name: Min Yang
email: yangmin@rcees.ac.cn
affiliations:
- ref: kleac
- ref: ucas
attributes:
corresponding: true
affiliations:
- id: kleac
name: State Key Laboratory of Environmental Aquatic Chemistry, Research Center for Eco-Environmental Sciences, Chinese Academy of Sciences
# address: No. 18 Shuangqing Road
city: Beijing
postal-code: 100085
country: China
url: https://www.skleac.ac.cn
- id: ucas
name: University of Chinese Academy of Sciences
# address: No. 19A Yuquan Road
city: Beijing
postal-code: 100049
country: China
url: https://www.ucas.ac.cn
prefer-html: true
format:
# elsevier-html:
# toc: true
# css: _extensions/drwater/dwms/inst/css/style.css
# docx:
# reference-doc: _extensions/drwater/dwms/inst/word/MS.docx
pdf:
fontsize: 12pt
keep-md: false
keep-tex: false
filters:
# - latex-environment
- authoraffil
---
```{r}
#| include: false
#| cache: false
lang <- "en"
isRendering <- isTRUE(getOption("knitr.in.progress"))
require(tidyverse)
require(drwateR)
require(patchwork)
rmdify::rmd_init()
dwfun::init()
```
{{< pagebreak >}}
# Abstract {-}
{{< pagebreak >}}
{{< pagebreak >}}
<!-- {{< include _est.qmd >}} -->
# References {-}
::: {#refs}
:::
{{< pagebreak >}}
<!-- {{< include _nature.qmd >}} -->
+49
View File
@@ -0,0 +1,49 @@
--[[
authors-block affiliations block extension for quarto
Copyright (c) 2023 Lorenz A. Kapsner
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local from_scholarly = require("from_scholarly_metadata")
local resolve_institutes = from_scholarly.resolve_institutes
local M = {}
local function normalize_affiliations(affiliations)
local affiliations_norm = List:new(affiliations or {}):map(function(affil, i)
affil.index = pandoc.MetaString(tostring(i))
affil.id = pandoc.MetaString(stringify(affil.id or affil.name))
return affil
end)
return affiliations_norm
end
M.normalize_affiliations = normalize_affiliations
local function has_key(set, key)
return set[key] ~= nil
end
M.has_key = has_key
local function normalize_authors(affiliations)
return function(auth)
auth.id = pandoc.MetaString(stringify(auth.name))
auth.affiliations = resolve_institutes(auth.affiliations or {}, affiliations)
return auth
end
end
M.normalize_authors = normalize_authors
return M