add extensions

This commit is contained in:
2024-03-21 00:29:18 +08:00
parent 4dfe6bdad6
commit 74c343770f
92 changed files with 15715 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
name: abstract-section
author: Albert Krewinkel
version: 1.0.0
contributes:
filters:
- abstract-section.lua

View File

@@ -0,0 +1,59 @@
--[[
abstract-to-meta move an "abstract" section into document metadata
Copyright: © 20172021 Albert Krewinkel
License: MIT see LICENSE file for details
]]
local abstract = {}
--- Extract abstract from a list of blocks.
function abstract_from_blocklist (blocks)
local body_blocks = {}
local looking_at_abstract = false
for _, block in ipairs(blocks) do
if block.t == 'Header' and block.level == 1 then
if block.identifier == 'abstract' then
looking_at_abstract = true
else
looking_at_abstract = false
body_blocks[#body_blocks + 1] = block
end
elseif looking_at_abstract then
if block.t == 'HorizontalRule' then
looking_at_abstract = false
else
abstract[#abstract + 1] = block
end
else
body_blocks[#body_blocks + 1] = block
end
end
return body_blocks
end
if PANDOC_VERSION >= {2,9,2} then
-- Check all block lists with pandoc 2.9.2 or later
return {{
Blocks = abstract_from_blocklist,
Meta = function (meta)
if not meta.abstract and #abstract > 0 then
meta.abstract = pandoc.MetaBlocks(abstract)
end
return meta
end
}}
else
-- otherwise, just check the top-level block-list
return {{
Pandoc = function (doc)
local meta = doc.meta
local other_blocks = abstract_from_blocklist(doc.blocks)
if not meta.abstract and #abstract > 0 then
meta.abstract = pandoc.MetaBlocks(abstract)
end
return pandoc.Pandoc(other_blocks, meta)
end,
}}
end

View File

@@ -0,0 +1,6 @@
name: author-info-blocks
author: Albert Krewinkel
version: 1.0.0
contributes:
filters:
- author-info-blocks.lua

View File

@@ -0,0 +1,179 @@
local List = require("pandoc.List")
local utils = require("pandoc.utils")
local stringify = utils.stringify
local byAuthor
local byAffiliation
local Authors = {}
local Affiliations = {}
local authorHoriz
local Corresponding = nil
local function make_correspondance(name, email)
correspondance = List:new({
pandoc.Str("* Corresponding Author: "),
pandoc.Str(name),
pandoc.Str(" ("),
pandoc.Link(email, "mailto:" .. email),
pandoc.Str(")"),
})
Corresponding = List:new({ pandoc.Para(correspondance) })
end
local equalCont
local function make_equal_contributor()
eq_statement = pandoc.Str("† These authors contributed equally to this work.")
equalCont = List:new({ pandoc.Para(eq_statement) })
end
local function create_author_list(byAuthor)
local authors = {}
for i, author in ipairs(byAuthor) do
local sups = {}
for j, aff in ipairs(author.affiliations) do
table.insert(sups, aff.number)
end
sups_str = table.concat(sups, ",")
local name = stringify(author.name.literal)
if author.attributes ~= nil then
if author.attributes["equal-contributor"] ~= nil and author.attributes["equal-contributor"] then
sups_str = sups_str .. ",†"
make_equal_contributor()
end
if author.attributes.corresponding ~= nil and author.attributes.corresponding then
local email = stringify(author.email)
sups_str = sups_str .. ",*"
make_correspondance(name, email)
end
end
local authorEntry = List:new({
pandoc.Str(name),
pandoc.Superscript(pandoc.Str(sups_str)),
})
if authorHoriz and i < #byAuthor then
authorEntry:extend({ pandoc.Str(", ") })
end
table.insert(authors, pandoc.Span(authorEntry))
end
if authorHoriz then
Authors = { pandoc.Para(authors) }
else
Authors = authors
end
end
local function create_affiliation_list(byAffiliation)
for i, aff in ipairs(byAffiliation) do
local full_aff = pandoc.List({})
if aff.name then
full_aff:insert(stringify(aff.name))
end
if aff.address then
full_aff:insert(stringify(aff.address))
end
if aff.city then
full_aff:insert(stringify(aff.city))
end
if aff["postal-code"] then
full_aff:insert(stringify(aff["postal-code"]))
end
if aff.region then
full_aff:insert(stringify(aff.region))
end
if aff.country then
full_aff:insert(stringify(aff.country))
end
local entry = table.concat(full_aff, ", ")
entry = aff.number .. ". " .. entry .. "."
table.insert(Affiliations, pandoc.Para(pandoc.Str(entry)))
end
end
local Abstract = nil
local function create_abstract(ab)
Abstract = {}
table.insert(Abstract, pandoc.Header(1, "Abstract"))
table.insert(Abstract, pandoc.Para(ab))
end
local Keywords = nil
local function create_keyword_list(kw)
Keywords = {}
-- quarto.log.output(kw)
local kws = pandoc.List({})
for i, keyword in ipairs(kw) do
kws:insert(stringify(keyword))
end
local kwentry = table.concat(kws, "; ")
kwentry = "Keywords: " .. kwentry .. "."
table.insert(Keywords, pandoc.Para(pandoc.Str(kwentry)))
end
local function remove_author_meta(meta)
meta.author = nil
meta.authors = nil
meta.affiliations = nil
meta["by-author"] = nil
meta["by-affiliation"] = nil
meta["abstract"] = nil
return meta
end
return {
{
Meta = function(meta)
byAuthor = meta["by-author"]
byAffiliation = meta["by-affiliation"]
if meta["author-horizontal"] ~= nil then
authorHoriz = meta["author-horizontal"]
else
authorHoriz = true
end
create_author_list(byAuthor)
create_affiliation_list(byAffiliation)
if meta["abstract"] ~= nil then
create_abstract(meta["abstract"])
end
if meta["keywords"] ~= nil then
create_keyword_list(meta["keywords"])
end
return meta
end,
},
{
Pandoc = function(doc)
local meta = doc.meta
local body = List:new({})
body:extend(Authors)
body:extend(Affiliations)
if equalCont ~= nil then
body:extend(equalCont)
end
if Corresponding ~= nil then
body:extend(Corresponding)
end
if Abstract then
body:extend(Abstract)
end
if Keywords then
body:extend(Keywords)
end
body:extend(doc.blocks)
meta = remove_author_meta(meta)
return pandoc.Pandoc(body, meta)
end,
},
}

View File

@@ -0,0 +1,6 @@
name: scholarly-metadata
author: Albert Krewinkel
version: 1.0.0
contributes:
filters:
- scholarly-metadata.lua

View File

@@ -0,0 +1,197 @@
--[[
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")
--- Returns the type of a metadata value.
--
-- @param v a metadata value
-- @treturn string one of `Blocks`, `Inlines`, `List`, `Map`, `string`, `boolean`
local function metatype(v)
if PANDOC_VERSION <= "2.16.2" then
local metatag = type(v) == "table" and v.t and v.t:gsub("^Meta", "")
return metatag and metatag ~= "Map" and metatag or type(v)
end
return pandoc.utils.type(v)
end
local type = pandoc.utils.type or metatype
-- Split a string at commas.
local function comma_separated_values(str)
local acc = List:new({})
for substr in str:gmatch("([^,]*)") do
acc[#acc + 1] = substr:gsub("^%s*", ""):gsub("%s*$", "") -- trim
end
return acc
end
--- Ensure the return value is a list.
local function ensure_list(val)
if type(val) == "List" then
return val
elseif type(val) == "Inlines" then
-- check if this is really a comma-separated list
local csv = comma_separated_values(pandoc.utils.stringify(val))
if #csv >= 2 then
return csv
end
return List:new({ val })
elseif type(val) == "table" and #val > 0 then
return List:new(val)
else
-- Anything else, use as a singleton (or empty list if val == nil).
return List:new({ val })
end
end
--- Returns a function which checks whether an object has the given ID.
local function has_id(id)
return function(x)
return x.id == id
end
end
--- Copy all key-value pairs of the first table into the second iff there is no
-- such key yet in the second table.
-- @returns the second argument
function add_missing_entries(a, b)
for k, v in pairs(a) do
b[k] = b[k] or v
end
return b
end
--- Create an object with a name. The name is either taken directly from the
-- `name` field, or from the *only* field name (i.e., key) if the object is a
-- dictionary with just one entry. If neither exists, the name is left unset
-- (`nil`).
function to_named_object(obj)
local named = {}
if type(obj) == "Inlines" then
-- Treat inlines as the name
named.name = obj
named.id = pandoc.utils.stringify(obj)
elseif type(obj) ~= "table" then
-- if the object isn't a table, just use its value as a name.
named.name = pandoc.MetaInlines({ pandoc.Str(tostring(obj)) })
named.id = tostring(obj)
elseif obj.name ~= nil then
-- object has name attribute → just create a copy of the object
add_missing_entries(obj, named)
named.id = pandoc.utils.stringify(named.id or named.name)
elseif next(obj) and next(obj, next(obj)) == nil then
-- Single-entry table. The entry's key is taken as the name, the value
-- contains the attributes.
key, attribs = next(obj)
if type(attribs) == "string" or type(attribs) == "Inlines" then
named.name = attribs
else
add_missing_entries(attribs, named)
named.name = named.name or pandoc.MetaInlines({ pandoc.Str(tostring(key)) })
end
named.id = named.id and pandoc.utils.stringify(named.id) or key
else
-- this is not a named object adhering to the usual conventions.
error("not a named object: " .. tostring(obj))
end
return named
end
--- Resolve affiliations placeholders to full named objects
local function resolve_affiliations(affiliations, known_affiliations)
local unresolved_affiliations
if affiliations == nil then
unresolved_affiliations = {}
elseif type(affiliations) == "string" or type(affiliations) == "number" then
unresolved_affiliations = { affiliations }
else
unresolved_affiliations = affiliations
end
local result = List:new({})
for i, inst in ipairs(unresolved_affiliations) do
result[i] = known_affiliations[tonumber(inst)]
or known_affiliations:find_if(has_id(pandoc.utils.stringify(inst)))
or to_named_object(inst)
end
return result
end
--- Insert a named object into a list; if an object of the same name exists
-- already, add all properties only present in the new object to the existing
-- item.
function merge_on_id(list, namedObj)
local elem, idx = list:find_if(has_id(namedObj.id))
local res = elem and add_missing_entries(namedObj, elem) or namedObj
local obj_idx = idx or (#list + 1)
-- return res, obj_idx
list[obj_idx] = res
return res, #list
end
--- Flatten a list of lists.
local function flatten(lists)
local result = List:new({})
for _, lst in ipairs(lists) do
result:extend(lst)
end
return result
end
--- Canonicalize authors and affiliations
local function canonicalize(raw_author, raw_affiliations)
local affiliations = ensure_list(raw_affiliations):map(to_named_object)
local authors = ensure_list(raw_author):map(to_named_object)
for _, author in ipairs(authors) do
author.affiliations = resolve_affiliations(ensure_list(author.affiliations), affiliations)
end
-- Merge affiliations defined in author objects with those defined in the
-- top-level list.
local author_insts = flatten(authors:map(function(x)
return x.affiliations
end))
for _, inst in ipairs(author_insts) do
merge_on_id(affiliations, inst)
end
-- Add list indices to affiliations for numbering and reference purposes
for idx, inst in ipairs(affiliations) do
inst.index = pandoc.MetaInlines({ pandoc.Str(tostring(idx)) })
end
-- replace affiliations with their indices
local to_index = function(inst)
return tostring(select(2, affiliations:find_if(has_id(inst.id))))
end
for _, author in ipairs(authors) do
author.affiliations = pandoc.MetaList(author.affiliations:map(to_index))
end
return authors, affiliations
end
return {
{
Meta = function(meta)
meta.author, meta.affiliations = canonicalize(meta.author, meta.affiliations)
return meta
end,
},
}