drwater/authoraffil/from_author_info_blocks.lua

298 lines
10 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
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