From 98a7ebe0d8094e15cbf1ef96dc946156848a51aa Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Sat, 15 Feb 2025 14:09:24 +0100 Subject: [PATCH 1/4] fix(packages): Suppress empty CSL macros --- packages/bibtex/csl/engine.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/bibtex/csl/engine.lua b/packages/bibtex/csl/engine.lua index 5c4824441..9a297f7e8 100644 --- a/packages/bibtex/csl/engine.lua +++ b/packages/bibtex/csl/engine.lua @@ -451,7 +451,17 @@ function CslEngine:_text (options, content, entry) local link if options.macro then if self.macros[options.macro] then + -- This is not explicit in the CSL 1.0.2 specification, which mention conditional + -- rendering for groups only. However, macro should behave as it own group, and + -- be suppressed on the same conditions. This is used in a variety of styles, for + -- instance UFES-ABNT, UNEAL-ABNT or ABNT-IPEA have definitions like: + -- + -- + -- (...) + -- + self:_enterGroup() t = self:_render_children(self.macros[options.macro], entry) + t = self:_leaveGroup(t) else SU.error("CSL macro " .. options.macro .. " not found") end From d8386ab18403c450b19e6bdef78d7d54c3a7b6bb Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Sat, 15 Feb 2025 16:03:00 +0100 Subject: [PATCH 2/4] fix(packages): Correct invalid links on CSL DOI, PMID, PMCID with affixes --- packages/bibtex/csl/engine.lua | 55 ++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/bibtex/csl/engine.lua b/packages/bibtex/csl/engine.lua index 9a297f7e8..0bb12aab6 100644 --- a/packages/bibtex/csl/engine.lua +++ b/packages/bibtex/csl/engine.lua @@ -448,7 +448,7 @@ end function CslEngine:_text (options, content, entry) local t - local link + local variable if options.macro then if self.macros[options.macro] then -- This is not explicit in the CSL 1.0.2 specification, which mention conditional @@ -468,7 +468,7 @@ function CslEngine:_text (options, content, entry) elseif options.term then t = self:_render_term(options.term, options.form, options.plural) elseif options.variable then - local variable = options.variable + variable = options.variable t = entry[variable] self:_addGroupVariable(variable, t) if variable == "locator" then @@ -486,26 +486,51 @@ function CslEngine:_text (options, content, entry) -- title). If the “short” form is selected but unavailable, the -- “long” form is rendered instead." -- But CSL-JSON etc. do not seem to have standard provision for it. - - if t and (variable == "URL" or variable == "DOI" or variable == "PMID" or variable == "PMCID") then - link = variable - end elseif options.value then t = options.value else SU.error("CSL text without macro, term, variable or value") end + -- Some styles have strip-periods even on DOI, etc. t = self:_render_stripPeriods(t, options) - t = self:_render_textCase(t, options) - t = self:_render_formatting(t, options) - t = self:_render_quotes(t, options) - t = self:_render_affixes(t, options) - if link then - t = self:_render_link(t, link) - elseif t and options.variable then - t = self:_render_text_specials(t) + if t then + if variable and (variable == "DOI" or variable == "PMID" or variable == "PMCID") then + -- Some styles have a "http..." as prefix for DOIs, etc. + -- Other add raw text such as "DOI: " + -- Call that a totally ill-defined feature of CSL, with unclear semantics + -- and conflating affixes for styling/presentation and the link itself. + local isURLPrefix = options.prefix and options.prefix:find("^http") + if isURLPrefix then + -- Make the prefix part of the link, we'll want it part of an hyperlink + t = options.prefix .. t + end + t = self:_render_link(t, variable) + t = self:_render_textCase(t, options) + t = self:_render_formatting(t, options) + t = self:_render_quotes(t, options) + t = self:_render_affixes(t, { + prefix = not isURLPrefix and options.prefix or nil, + suffix = options.suffix, + }) + -- (No "text specials" in DOIs, etc. by nature) + elseif variable == "URL" then + t = self:_render_link(t, variable) + t = self:_render_textCase(t, options) + t = self:_render_formatting(t, options) + t = self:_render_quotes(t, options) + t = self:_render_affixes(t, options) + -- (No "text specials" in URLs by nature) + else + t = self:_render_textCase(t, options) + t = self:_render_formatting(t, options) + t = self:_render_quotes(t, options) + t = self:_render_affixes(t, options) + if t and options.variable then + t = self:_render_text_specials(t) + end + end + t = self:_render_display(t, options) end - t = self:_render_display(t, options) return t end From 21b751eb8f423932c227448ea6e664d706772152 Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Sat, 15 Feb 2025 18:08:17 +0100 Subject: [PATCH 3/4] fix(packages): Correct error handling the locator on some CSL styles --- packages/bibtex/csl/engine.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bibtex/csl/engine.lua b/packages/bibtex/csl/engine.lua index 0bb12aab6..42dbcd92b 100644 --- a/packages/bibtex/csl/engine.lua +++ b/packages/bibtex/csl/engine.lua @@ -472,8 +472,8 @@ function CslEngine:_text (options, content, entry) t = entry[variable] self:_addGroupVariable(variable, t) if variable == "locator" then + variable = t and t.label t = t and t.value - variable = entry.locator.label end if variable == "page" and t then -- Replace any dash in page ranges @@ -732,6 +732,7 @@ function CslEngine:_number (options, content, entry) local value = entry[variable] self:_addGroupVariable(variable, value) if variable == "locator" then -- special case + variable = value and value.label value = value and value.value end if value then From 5de5607fde3eadb93f77b7260a326c43396cf229 Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Sat, 15 Feb 2025 20:17:39 +0100 Subject: [PATCH 4/4] fix(packages): Honor affixes on multiple CSL citations correctly (Well, we don't support yet citing multiple keys in our package, but the formatting would have been wrong.) --- packages/bibtex/csl/engine.lua | 42 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/bibtex/csl/engine.lua b/packages/bibtex/csl/engine.lua index 42dbcd92b..650a2a385 100644 --- a/packages/bibtex/csl/engine.lua +++ b/packages/bibtex/csl/engine.lua @@ -421,29 +421,43 @@ end function CslEngine:_layout (options, content, entries) local output = {} + if self.mode == "citation" then + for _, entry in ipairs(entries) do + self:_prerender() + local elem = self:_render_children(content, entry) + elem = self:_postrender(elem) + if elem then + table.insert(output, elem) + end + end + -- The CSL 1.0.2 specification is not very clear on this point, but on + -- citations, affixes and formatting apply on the whole layout. + -- Affixes are arround the delimited list, e.g. "(Smith, 2000; Jones, 2001)" + -- Rendering is done after, so vertical-align, etc. apply to the whole list, + -- e.g. 1,2 + local cites = self:_render_delimiter(output, options.delimiter or "; ") + cites = self:_render_affixes(cites, options) + cites = self:_render_formatting(cites, options) + return cites + end + -- On bibliographies, affixes (usually just a period suffix) apply on each entry. + -- Formatting is not forbidden in the specification, and occurs in a few styles. + -- But it doesn't seem to be very useful (mostly font-variant="normal" and + -- vertical-align="baseline"). Anyhow, if set, it probably applies to the + -- entry including the affixes. + -- CSL 1.0.2 only mentions a delimiter for citations, so it's not used here, + -- quite logically as we force a paragraph break between entries. for _, entry in ipairs(entries) do self:_prerender() local elem = self:_render_children(content, entry) - -- affixes and formatting likely apply on elementary entries - -- (The CSL 1.0.2 specification is not very clear on this point.) - elem = self:_render_formatting(elem, options) elem = self:_render_affixes(elem, options) + elem = self:_render_formatting(elem, options) elem = self:_postrender(elem) if elem then table.insert(output, elem) end end - if options.delimiter then - return self:_render_delimiter(output, options.delimiter) - end - -- (Normally citations have a delimiter options, so we should only reach - -- this point for the bibliography) - local delim = self.mode == "citation" and "; " or "" - -- references all belong to a different paragraph - -- FIXME: should account for attributes on the toplevel bibliography element: - -- line-spacing - -- hanging-indent - return table.concat(output, delim) + return table.concat(output, "") end function CslEngine:_text (options, content, entry)