diff options
Diffstat (limited to 'biblio/citation-style-language/citeproc-bibtex2csl.lua')
-rw-r--r-- | biblio/citation-style-language/citeproc-bibtex2csl.lua | 259 |
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 |