summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-bibtex2csl.lua
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-04-14 03:01:48 +0000
committerNorbert Preining <norbert@preining.info>2023-04-14 03:01:48 +0000
commitb17717e3829bc08efb71ec2537d9d894c8b82d45 (patch)
tree42351b1c085da8ee5f80387c2fb9ee00b7a7767a /biblio/citation-style-language/citeproc-bibtex2csl.lua
parentc6d2f94d04f69bb5b2143ef62e7ab90bd3b60212 (diff)
CTAN sync 202304140301
Diffstat (limited to 'biblio/citation-style-language/citeproc-bibtex2csl.lua')
-rw-r--r--biblio/citation-style-language/citeproc-bibtex2csl.lua259
1 files changed, 201 insertions, 58 deletions
diff --git a/biblio/citation-style-language/citeproc-bibtex2csl.lua b/biblio/citation-style-language/citeproc-bibtex2csl.lua
index 06c6de1576..83fada68ca 100644
--- a/biblio/citation-style-language/citeproc-bibtex2csl.lua
+++ b/biblio/citation-style-language/citeproc-bibtex2csl.lua
@@ -6,13 +6,28 @@
local bibtex2csl = {}
-local bibtex_parser = require("citeproc-bibtex-parser")
-local bibtex_data = require("citeproc-bibtex-data")
-local latex_parser = nil -- load as needed
-local util = require("citeproc-util")
+local uni_utf8
+local bibtex_parser
+local bibtex_data
+local latex_parser
+local unicode
+local util
+if kpse then
+ uni_utf8 = require("unicode").utf8
+ bibtex_parser = require("citeproc-bibtex-parser")
+ bibtex_data = require("citeproc-bibtex-data")
+ unicode = require("citeproc-unicode")
+ util = require("citeproc-util")
+else
+ uni_utf8 = require("lua-utf8")
+ bibtex_parser = require("citeproc.bibtex-parser")
+ bibtex_data = require("citeproc.bibtex-data")
+ unicode = require("citeproc.unicode")
+ util = require("citeproc.util")
+end
----@alias CslItem table
+---@alias CslItem table<string, nil | string | number | table>
---@alias CslData CslItem[]
@@ -48,24 +63,48 @@ end
---@return CslData
function bibtex2csl.convert_to_csl_data(bib, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case)
local csl_data = {}
+
+ -- BibTeX looks for crossref in a case-insensitive manner.
+ local entries_by_id = {}
+ for _, entry in ipairs(bib.entries) do
+ entries_by_id[unicode.casefold(entry.key)] = entry
+ end
+
for _, entry in ipairs(bib.entries) do
+ ---@type CslItem
local item = {
id = entry.key,
type = "document",
}
- -- CSL types
- local type_data = bibtex_data.types[entry.type]
- if type_data and type_data.csl then
- item.type = type_data.csl
+ -- crossref
+ if entry.fields.crossref then
+ bibtex2csl.process_cross_ref(entry, entries_by_id)
end
- --TODO: language
+ bibtex2csl.pre_process_special_fields(item, entry)
+
+ -- First convert primary fields
+ for field, csl_field in pairs(bibtex_data.primary_fields) do
+ local value = entry.fields[field]
+ if value then
+ local _, csl_value = bibtex2csl.convert_field(
+ field, value, keep_unknown_commands, case_protection, sentence_case_title, item.language, check_sentence_case)
+ if csl_field and csl_value and not item[csl_field] then
+ item[csl_field] = csl_value
+ end
+ end
+ end
- -- TODO: preprosse
- -- Merge title, maintitle, substitle, titleaddon
+ -- Convert the fields in a fixed order
+ local field_list = {}
+ for field, _ in pairs(entry.fields) do
+ table.insert(field_list, field)
+ end
+ table.sort(field_list)
- for field, value in pairs(entry.fields) do
+ for _, field in ipairs(field_list) do
+ local value = entry.fields[field]
local csl_field, csl_value = bibtex2csl.convert_field(
field, value, keep_unknown_commands, case_protection, sentence_case_title, item.language, check_sentence_case)
if csl_field and csl_value and not item[csl_field] then
@@ -73,7 +112,7 @@ function bibtex2csl.convert_to_csl_data(bib, keep_unknown_commands, case_protect
end
end
- bibtex2csl.process_special_fields(item, entry.fields)
+ bibtex2csl.post_process_special_fields(item, entry, entry.type)
table.insert(csl_data, item)
end
@@ -81,6 +120,98 @@ function bibtex2csl.convert_to_csl_data(bib, keep_unknown_commands, case_protect
end
+---@param entry BibtexEntry
+---@param entries_by_id table<string, BibtexEntry>
+function bibtex2csl.process_cross_ref(entry, entries_by_id)
+ local ref_entry = entries_by_id[unicode.casefold(entry.fields.crossref)]
+ if ref_entry then
+ for field, value in pairs(ref_entry.fields) do
+ if not entry.fields[field] then
+ entry.fields[field] = value
+ end
+ end
+ else
+ util.error(string.format('Crossref "%s" not found.', entry.fields.crossref))
+ end
+end
+
+
+---@param item CslItem
+---@param entry BibtexEntry
+function bibtex2csl.pre_process_special_fields(item, entry)
+ -- CSL types
+ local type_data = bibtex_data.types[entry.type]
+ if type_data and type_data.csl then
+ item.type = type_data.csl
+ elseif entry.fields.url then
+ item.type = "webpage"
+ end
+
+ -- BibTeX's `edition` is expected to be an ordinal.
+ if entry.fields.edition then
+ item.edition = util.convert_ordinal_to_arabic(entry.fields.edition)
+ end
+
+ -- language: convert `babel` language to ISO 639-1 language code
+ local lang = entry.fields.langid or entry.fields.language
+ if lang then
+ item.language = bibtex_data.language_code_map[unicode.casefold(lang)]
+ end
+ -- if not item.language then
+ -- if util.has_cjk_char(item.title) then
+ -- item.language = "zh"
+ -- end
+ -- end
+
+ -- Merge title, maintitle, subtitle, titleaddon
+ bibtex2csl.process_titles(entry)
+
+end
+
+
+---@param entry BibtexEntry
+function bibtex2csl.process_titles(entry)
+ local fields = entry.fields
+ if fields.subtitle then
+ if not fields.shorttitle then
+ fields.shorttitle = fields.title
+ end
+ if fields.title then
+ fields.title = util.join_title(fields.title, fields.subtitle)
+ else
+ fields.title = fields.subtitle
+ end
+ end
+ if fields.booksubtitle then
+ if not fields.shorttitle then
+ fields["container-title-short"] = fields.booktitle
+ end
+ if fields.booktitle then
+ fields.booktitle = util.join_title(fields.booktitle, fields.booksubtitle)
+ else
+ fields.booktitle = fields.booksubtitle
+ end
+ end
+ if fields.journalsubtitle then
+ if fields.journaltitle then
+ fields.journaltitle = util.join_title(fields.journaltitle, fields.journalsubtitle)
+ elseif fields.journal then
+ fields.journal = util.join_title(fields.journal, fields.journal)
+ end
+ end
+ if fields.issuesubtitle then
+ if not fields.shorttitle then
+ fields["volume-title-short"] = fields.issuetitle
+ end
+ if fields.issuetitle then
+ fields.issuetitle = util.join_title(fields.issuetitle, fields.issuesubtitle)
+ else
+ fields.issuetitle = fields.issuesubtitle
+ end
+ end
+end
+
+
---Convert BibTeX field to CSL field
---@param bib_field string
---@param value string
@@ -101,7 +232,11 @@ function bibtex2csl.convert_field(bib_field, value, keep_unknown_commands, case_
return nil, nil
end
- latex_parser = latex_parser or require("citeproc-latex-parser")
+ if kpse then
+ latex_parser = latex_parser or require("citeproc-latex-parser")
+ else
+ latex_parser = latex_parser or require("citeproc.latex-parser")
+ end
local field_type = field_data.type
local csl_value
@@ -115,7 +250,12 @@ function bibtex2csl.convert_field(bib_field, value, keep_unknown_commands, case_
csl_value[i] = bibtex2csl.convert_to_csl_name(name_dict)
end
- elseif bib_field == "title" or bib_field == "booktitle" then
+ elseif field_type == "date" then
+ csl_value = latex_parser.latex_to_pseudo_html(value, false, false)
+ csl_value = bibtex2csl._parse_edtf_date(csl_value)
+
+ elseif bib_field == "title" or bib_field == "shorttitle"
+ or bib_field == "booktitle" or bib_field == "container-title-short" then
-- util.debug(value)
-- 1. unicode 2. sentence case 3. html tag
if sentence_case_title and (not language or util.startswith(language, "en")) then
@@ -129,9 +269,7 @@ function bibtex2csl.convert_field(bib_field, value, keep_unknown_commands, case_
else
-- 1. unicode 2. html tag
csl_value = latex_parser.latex_to_pseudo_html(value, keep_unknown_commands, case_protection)
- if field_type == "date" then
- csl_value = bibtex2csl._parse_edtf_date(csl_value)
- elseif csl_field == "volume" or csl_field == "page" then
+ if csl_field == "volume" or csl_field == "page" then
csl_value = string.gsub(csl_value, util.unicode["en dash"], "-")
end
end
@@ -166,56 +304,47 @@ function bibtex2csl.convert_to_csl_name(bibtex_name)
end
-function bibtex2csl.process_special_fields(item, bib_fields)
- -- Default entry type `document`
- if item.type == "document" then
- if item.URL then
- item.type = "webpage"
- else
- item.type = "article"
- end
- end
-
+---@param item CslItem
+---@param entry BibtexEntry
+function bibtex2csl.post_process_special_fields(item, entry)
+ local bib_type = entry.type
+ local bib_fields = entry.fields
-- event-title: for compatibility with CSL v1.0.1 and earlier versions
if item["event-title"] then
item.event = item["event-title"]
end
- -- issued date
- if bib_fields.year and not item.issued then
- item.issued = bibtex2csl._parse_edtf_date(bib_fields.year)
+ -- Jounal abbreviations
+ if item.type == "article-journal" or item.type == "article-magazine"
+ or item.type == "article-newspaper" then
+ util.check_journal_abbreviations(item)
end
- local month = bib_fields.month
- if month and string.match(month, "^%d+$") then
- if item.issued and item.issued["date-parts"] and
+
+ -- month
+ -- local month = bib_fields.month
+ local month_text = bib_fields.month
+ if month_text then
+ month_text = latex_parser.latex_to_pseudo_html(month_text, false, false)
+ local month, day = uni_utf8.match(month_text, "^(%a+)%.?,?%s+(%d+)%a*$")
+ if not month then
+ day, month = uni_utf8.match(month_text, "^(%d+)%a*%s+(%a+)%.?$")
+ end
+ if not month then
+ month = string.match(month_text, "^(%a+)%.?$")
+ end
+ if month then
+ month = bibtex_data.months[unicode.casefold(month)]
+ end
+ if month and item.issued and item.issued["date-parts"] and
item.issued["date-parts"][1] and
item.issued["date-parts"][1][2] == nil then
item.issued["date-parts"][1][2] = tonumber(month)
+ if day then
+ item.issued["date-parts"][1][3] = tonumber(day)
+ end
end
end
- -- language: convert `babel` language to ISO 639-1 language code
- if not item.language and bib_fields.language then
- item.language = bib_fields.language
- end
- if item.language then
- local language_code = bibtex_data.language_code_map[item.language]
- if language_code then
- item.language = language_code
- end
- end
- -- if not item.language then
- -- if util.has_cjk_char(item.title) then
- -- item.language = "zh"
- -- end
- -- end
-
- -- Jounal abbreviations
- if item.type == "article-journal" or item.type == "article-magazine"
- or item.type == "article-newspaper" then
- util.check_journal_abbreviations(item)
- end
-
-- number
if item.number then
if item.type == "article-journal" or item.type == "article-magazine" or
@@ -230,8 +359,22 @@ function bibtex2csl.process_special_fields(item, bib_fields)
end
end
+ -- organization: the `organizer` that sponsors a conference or a `publisher` that publishes a `@manual` or `@online`.
+ if bib_fields.organization then
+ if item.publisher or bib_type == "inproceedings" or bib_type == "proceedings" then
+ if not item.organizer then
+ item.organizer = {
+ literal = bib_fields.organization
+ }
+ end
+ elseif not item.publisher then
+ item.publisher = bib_fields.organization
+ end
+ end
+
-- PMID
- if bib_fields.eprint and string.lower(bib_fields.eprinttype) == "pubmed" and not item.PMID then
+ if bib_fields.eprint and type(bib_fields.eprinttype) == "string" and
+ string.lower(bib_fields.eprinttype) == "pubmed" and not item.PMID then
item.PMID = bib_fields.eprint
end