diff options
Diffstat (limited to 'biblio/citation-style-language/citeproc-output.lua')
-rw-r--r-- | biblio/citation-style-language/citeproc-output.lua | 482 |
1 files changed, 381 insertions, 101 deletions
diff --git a/biblio/citation-style-language/citeproc-output.lua b/biblio/citation-style-language/citeproc-output.lua index d3ef1d5ede..b162328e66 100644 --- a/biblio/citation-style-language/citeproc-output.lua +++ b/biblio/citation-style-language/citeproc-output.lua @@ -1,5 +1,5 @@ -- --- Copyright (c) 2021-2022 Zeping Lee +-- Copyright (c) 2021-2023 Zeping Lee -- Released under the MIT license. -- Repository: https://github.com/zepinglee/citeproc-lua -- @@ -7,6 +7,7 @@ local output_module = {} local unicode = require("unicode") +local citeproc_unicode = require("citeproc-unicode") local dom = require("luaxml-domobject") local util = require("citeproc-util") @@ -33,17 +34,27 @@ function LocalizedQuotes:new(outer_open, outer_close, inner_open, inner_close, p return o end - +-- Inspired by: +-- https://github.com/zotero/citeproc-rs/blob/master/crates/io/src/output/markup.rs#L67 +-- https://hackage.haskell.org/package/pandoc-types-1.22.2.1/docs/Text-Pandoc-Definition.html +---@class InlineElement local InlineElement = { _type = "InlineElement", _base_class = "InlineElement", + value = nil, + inlines = nil, } -function InlineElement:derive(type) + + +---comment +---@param class_name string +---@return table +function InlineElement:derive(class_name) local o = { - _type = type, + _type = class_name, } - self[type] = o + -- self[class_name] = o setmetatable(o, self) self.__index = self o.__index = o @@ -60,6 +71,26 @@ function InlineElement:new(inlines) end +function InlineElement:_debug() + local text = self._type .. "(" + if self.value then + text = text .. '"' .. self.value .. '"' + elseif self.inlines then + local inlines_text = "" + for _, inline in ipairs(self.inlines) do + if inlines_text ~= "" then + inlines_text = inlines_text .. ", " + end + inlines_text = inlines_text .. inline:_debug() + end + text = text .. inlines_text + end + text = text .. ")" + return text +end + + +---@class PlainText local PlainText = InlineElement:derive("PlainText") function PlainText:new(value) @@ -70,6 +101,7 @@ function PlainText:new(value) end +---@class Formatted local Formatted = InlineElement:derive("Formatted") function Formatted:new(inlines, formatting) @@ -81,6 +113,7 @@ function Formatted:new(inlines, formatting) end +---@class Micro local Micro = InlineElement:derive("Micro") -- This is how we can flip-flop only user-supplied styling. @@ -93,6 +126,7 @@ function Micro:new(inlines) end +---@class Quoted local Quoted = InlineElement:derive("Quoted") function Quoted:new(inlines, localized_quotes) @@ -110,6 +144,7 @@ function Quoted:new(inlines, localized_quotes) end +---@class Code local Code = InlineElement:derive("Code") function Code:new(value) @@ -120,6 +155,7 @@ function Code:new(value) end +---@class MathML local MathML = InlineElement:derive("MathML") function MathML:new(value) @@ -130,6 +166,7 @@ function MathML:new(value) end +---@class MathTeX local MathTeX = InlineElement:derive("MathTeX") function MathTeX:new(value) @@ -140,6 +177,7 @@ function MathTeX:new(value) end +---@class NoCase local NoCase = InlineElement:derive("NoCase") function NoCase:new(inlines) @@ -150,6 +188,7 @@ function NoCase:new(inlines) end +---@class NoDecor local NoDecor = InlineElement:derive("NoDecor") function NoDecor:new(inlines) @@ -160,6 +199,7 @@ function NoDecor:new(inlines) end +---@class Linked local Linked = InlineElement:derive("Linked") function Linked:new(value, href) @@ -171,6 +211,7 @@ function Linked:new(value, href) end +---@class Div local Div = InlineElement:derive("Div") function Div:new(inlines, display) @@ -182,6 +223,18 @@ function Div:new(inlines, display) end +---@class CiteInline +local CiteInline = InlineElement:derive("CiteInline") + +function CiteInline:new(inlines, cite_item) + local o = InlineElement.new(self) + o.inlines = inlines + o.cite_item = cite_item + setmetatable(o, self) + return o +end + + function InlineElement:parse(text, context) local text_type = type(text) local inlines @@ -190,7 +243,9 @@ function InlineElement:parse(text, context) inlines = self:parse_csl_rich_text(text) elseif text_type == "string" then -- String with HTML-like formatting tags + -- util.debug(text) inlines = self:parse_html_tags(text, context) + -- util.debug(inlines) elseif text_type == "number" then inlines = {PlainText:new(tostring(text))} else @@ -266,6 +321,7 @@ function InlineElement:parse_csl_rich_text(text) return inlines end +-- TODO: Rewrite with lpeg function InlineElement:parse_html_tags(str, context) -- Return a list of inlines -- if type(str) ~= "string" then @@ -294,7 +350,14 @@ function InlineElement:from_node(node) for _, child in ipairs(node:get_children()) do local inline if child:is_text() then - inline = PlainText:new(child:get_text()) + local text = child:get_text() + if tag_name == "code" or tag_name == "script" then + inline = Code:new(text) + elseif tag_name == "math" then + inline = MathTeX:new(text) + else + inline = PlainText:new(text) + end elseif child:is_element() then inline = InlineElement:from_node(child) end @@ -554,6 +617,17 @@ function InlineElement:capitalize_first_term() end end +--[[ +Class inheritance hierarchical + +OutputFormat +├── SortStringFormat +├── DisamStringFormat +└── Markup + ├── LatexWriter + ├── HtmlWriter + └── PlainTextWriter +--]] local OutputFormat = {} @@ -566,13 +640,13 @@ function OutputFormat:new(format_name) return o end -function OutputFormat:flatten_ir(ir) +function OutputFormat:flatten_ir(ir, override_delim) if self.group_var == "missing" or self.collapse_suppressed then return {} end local inlines = {} if ir._type == "SeqIr" or ir._type == "NameIr" then - inlines = self:flatten_seq_ir(ir) + inlines = self:flatten_seq_ir(ir, override_delim) else inlines = self:with_format(ir.inlines, ir.formatting) inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); @@ -581,7 +655,7 @@ function OutputFormat:flatten_ir(ir) return inlines end -function OutputFormat:flatten_seq_ir(ir) +function OutputFormat:flatten_seq_ir(ir, override_delim) -- if not ir.children then -- print(debug.traceback()) -- end @@ -589,9 +663,14 @@ function OutputFormat:flatten_seq_ir(ir) return {} end local inlines_list = {} + local delimiter = ir.delimiter + if not delimiter and ir.should_inherit_delim then + delimiter = override_delim + end + for _, child in ipairs(ir.children) do if child.group_var ~= "missing" and not child.collapse_suppressed then - local child_inlines = self:flatten_ir(child) + local child_inlines = self:flatten_ir(child, delimiter) if #child_inlines > 0 then table.insert(inlines_list, child_inlines) end @@ -602,7 +681,7 @@ function OutputFormat:flatten_seq_ir(ir) return {} end - local inlines = self:group(inlines_list, ir.delimiter, ir.formatting) + local inlines = self:group(inlines_list, delimiter, ir.formatting) -- assert ir.quotes == localized quotes inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); inlines = self:with_display(inlines, ir.display); @@ -644,6 +723,73 @@ function OutputFormat:with_format(inlines, formatting) return self:format_list(inlines, formatting) end + +---Check if there is a lowercase word not in CSL's stop words +---@param str string +---@return boolean +local function is_str_sentence_case(str) + local words = citeproc_unicode.words(str) + for _, word in ipairs(words) do + if util.is_lower(word) and not util.stop_words[word] then + -- util.debug(word) + return true + end + end + return false +end + +---Returns true if the inlines contain a lowercase word which is not CSL's stop words. +---@param inlines table[] +---@return boolean +function OutputFormat:is_sentence_case(inlines) + for _, inline in ipairs(inlines) do + if util.is_instance(inline, PlainText) then + if is_str_sentence_case(inline.value) then + -- util.debug(inline.value) + return true + end + + elseif util.is_instance(inline, Quoted) + or util.is_instance(inline, NoCase) + or util.is_instance(inline, NoDecor) + or util.is_instance(inline, CiteInline) + or (util.is_instance(inline, Formatted) + and inline.formatting["font-variant"] ~= "small-caps" + and inline.formatting["vertical-align"] ~= "sup" + and inline.formatting["vertical-align"] ~= "sub") then + if self:is_sentence_case(inline.inlines) then + return true + end + end + end + return false +end + +---1. For uppercase strings, the first character of the string remains capitalized. +--- All other letters are lowercased. +---2. For lower or mixed case strings, the first character of the first word is +--- capitalized if the word is lowercase. +---3. All other words are lowercased if capitalized. +---@param inlines table[] +---@param check_sentence_case boolean +function OutputFormat:convert_sentence_case(inlines, check_sentence_case) + if not inlines or #inlines == 0 then + return + end + local is_uppercase = false -- TODO + -- util.debug(check_sentence_case) + -- util.debug(self:is_sentence_case(inlines)) + if check_sentence_case then + if self:is_sentence_case(inlines) then + self:apply_text_case_inner(inlines, "capitalize-first", false, is_uppercase) + else + self:apply_text_case_inner(inlines, "sentence-strong", false, is_uppercase) + end + else + self:apply_text_case_inner(inlines, "sentence-strong", false, is_uppercase) + end +end + function OutputFormat:apply_text_case(inlines, text_case, is_english) if not inlines or #inlines == 0 or not text_case then return @@ -680,8 +826,12 @@ function OutputFormat:apply_text_case_inner(inlines, text_case, seen_one, is_upp end local is_last = (i == #inlines) if inline._type == "PlainText" then + -- util.debug(inline.value) + -- util.debug(text_case) inline.value = self:transform_case(inline.value, text_case, seen_one, is_last, is_uppercase); + -- util.debug(inline.value) seen_one = seen_one or string_contains_word(inline.value) + elseif inline._type == "NoCase" or inline._type == "NoDecor" or inline._type == "Code" or @@ -692,8 +842,9 @@ function OutputFormat:apply_text_case_inner(inlines, text_case, seen_one, is_upp (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sub") then seen_one = seen_one or inline_contains_word(inline) - elseif inline._type == "Formatted" or inline._type == "Quoted" then + elseif inline._type == "Formatted" or inline._type == "Quoted" or inline._type == "CiteInline" then seen_one = self:apply_text_case_inner(inline.inlines, text_case, seen_one, is_uppercase) or seen_one + end end return seen_one @@ -708,85 +859,136 @@ local function transform_uppercase(str) return string.gsub(str, utf8.charpattern, unicode.utf8.upper) end -local function transform_first_word(str, transform) - -- TODO: [Unicode word boundaries](https://www.unicode.org/reports/tr29/#Word_Boundaries) - local segments = util.segment_words(str) - for _, segment in ipairs(segments) do - if segment[1] ~= "" then - segment[1] = transform(segment[1]) - break +---comment +---@param str string +---@param is_first boolean +---@param transform function +---@return string +local function transform_first_word(str, is_first, transform) + if is_first then + local segments = citeproc_unicode.split_word_bounds(str) + for i, segment in ipairs(segments) do + if citeproc_unicode.isalnum(segment) then + segments[i] = transform(segment) + break + end end + str = table.concat(segments) end - local res = "" - for _, segment in ipairs(segments) do - res = res .. segment[1] .. segment[2] - end - return res + return str end -local function transform_each_word(str, seen_one, is_last, transform) - -- util.debug(str) - local segments = util.segment_words(str) + +local SegmentType = { + Word = 1, + Puctuation = 2, + EndingPunctuation = 3, -- "?" and "!" excluding "." Do we need a sentence break? + Colon = 4, -- Including em-dash + Other = 0, +} + +---comment +---@param str string +---@param seen_one boolean +---@param is_last_inline boolean +---@param transform function +---@return string +local function transform_each_word(str, seen_one, is_last_inline, transform) + local segments = citeproc_unicode.split_word_bounds(str) -- util.debug(segments) - -- print(debug.traceback()) - local first_idx - local last_idx + local segment_type_list = {} + local last_word_idx for i, segment in ipairs(segments) do - if segment[1] ~= "" then - if not first_idx then - first_idx = i + segment_type_list[i] = SegmentType.Other + + -- Do not use isalnum(): "can't" + if unicode.grapheme.match(segment, "%w") then + segment_type_list[i] = SegmentType.Word + last_word_idx = i + + elseif unicode.grapheme.match(segment, "%p") then + segment_type_list[i] = SegmentType.Puctuation + if segment == "!" or segment == "?" then + segment_type_list[i] = SegmentType.EndingPunctuation + -- elseif segment == ":" or segment == "—" then + elseif segment == ":" then + -- Em dash is not taken into consideration, see "Stability—with Job" in `textcase_TitleWithEmDash.txt` + segment_type_list[i] = SegmentType.Colon end - last_idx = i end end - local immediate_before = "" - local last_punct = "" + local sentence_begin = not seen_one + local after_colon = false + for i, segment in ipairs(segments) do - local is_first_word = not seen_one and i == first_idx - local is_last_word = is_last and i == last_idx - local follows_colon = ( - last_punct == ":" or - last_punct == "!" or - last_punct == "?" or - last_punct == "?") - local no_stop_word = is_first_word or is_last_word or follows_colon or (segment[2] == "-" and immediate_before ~= "-") - - if (immediate_before == "." or immediate_before == "-") and #segment[1] == 1 then - else - segment[1] = transform(segment[1], no_stop_word) - end + if segment_type_list[i] == SegmentType.Word then + local is_first_word = sentence_begin + local is_last_word = segment_type_list[i+1] == SegmentType.EndingPunctuation + or (is_last_inline and i == last_word_idx) + -- See "Pro-Environmental" in `textcase_StopWordBeforeHyphen.txt` + -- but not "Out-of-Fashion" in `textcase_TitleCaseWithHyphens.txt`. + local ignore_stop_word = segments[i+1] == "-" and segments[i-1] ~= "-" + + -- util.debug(segment) + -- util.debug(after_colon) + -- See "07-x" in `textcase_LastChar.txt` + if not (segments[i-1] == "-" and unicode.grapheme.len(segment) == 1) then + segments[i] = transform(segment, is_first_word, after_colon, is_last_word, ignore_stop_word) + + end + -- util.debug(segments[i]) + + sentence_begin = false + after_colon = false + + elseif segment_type_list[i] == SegmentType.EndingPunctuation then + sentence_begin = true + + elseif segment_type_list[i] == SegmentType.Colon then + after_colon = true - if segment[1] ~= "" then - immediate_before = segment[1] - last_punct = string.match(segment[1], "(%S)%s*$") or last_punct - end - if segment[2] ~= "" then - immediate_before = segment[2] - last_punct = string.match(segment[2], "(%S)%s*$") or last_punct end end - local res = "" - for _, segment in ipairs(segments) do - res = res .. segment[1] .. segment[2] - end - return res + return table.concat(segments) end +---comment +---@param word string +---@return string local function transform_capitalize_word_if_lower(word) - if util.is_lower(word) then - return string.gsub(word, utf8.charpattern, unicode.utf8.upper, 1) + if citeproc_unicode.islower(word) then + local res = string.gsub(word, utf8.charpattern, unicode.utf8.upper, 1) + return res else return word end end -local function title_case_word(word, no_stop_word) +local function title_case_word(word, is_first, after_end_punct, is_last, ignore_stop_word) -- Entirely non-English -- e.g. "β" in "β-Carotine" - if string.match(word, "%a") and (not util.stop_words[word] or no_stop_word) and util.is_lower(word) then - return string.gsub(word, utf8.charpattern, unicode.utf8.upper, 1) + -- TODO: two-word cases like "due to" + -- util.debug(word) + -- util.debug(ignore_stop_word) + if (is_first or is_last or after_end_punct or ignore_stop_word or not util.stop_words[word]) + and string.match(word, "%a") and citeproc_unicode.islower(word) then + return citeproc_unicode.capitalize(word) + else + return word + end +end + +local function transform_lowercase_if_capitalize(word, is_first, after_end_punct, is_last, is_stop_word) + if not (is_first or after_end_punct) then + local is_capitalize_word = false + local lower_first = string.gsub(word, utf8.charpattern, unicode.utf8.lower, 1) + if util.is_lower(lower_first) then + return lower_first + else + return word + end else return word end @@ -799,15 +1001,20 @@ function OutputFormat:transform_case(str, text_case, seen_one, is_last, is_upper elseif text_case == "uppercase" then res = transform_uppercase(str) elseif text_case == "capitalize-first" then - res = transform_first_word(str, transform_capitalize_word_if_lower) + res = transform_first_word(str, not seen_one, transform_capitalize_word_if_lower) elseif text_case == "capitalize-all" then - res = transform_each_word(str, false, false, transform_capitalize_word_if_lower) + res = transform_each_word(str, false, true, transform_capitalize_word_if_lower) elseif text_case == "sentence" then -- TODO: if uppercase convert all to lowercase - res = transform_first_word(str, transform_capitalize_word_if_lower) + res = transform_first_word(str, not seen_one, transform_capitalize_word_if_lower) elseif text_case == "title" then -- TODO: if uppercase convert all to lowercase res = transform_each_word(str, seen_one, is_last, title_case_word) + elseif text_case == "sentence-strong" then + -- Conversion for BibTeX title fields to sentence case + -- TODO: if uppercase convert all to lowercase + res = transform_each_word(str, seen_one, is_last, transform_lowercase_if_capitalize) + res = transform_first_word(res, not seen_one, transform_capitalize_word_if_lower) end return res end @@ -1136,7 +1343,8 @@ local function normalise_text_elements(inlines) idx = idx + 1 end - elseif first._type == "Formatted" and second._type == "PlainText" then + elseif (first._type == "Formatted" or first._type == "CiteInline") + and second._type == "PlainText" then local success = smash_just_punc(first, second) if success then if second.value == "" then @@ -1229,7 +1437,7 @@ function OutputFormat:move_punctuation(inlines, piq) for _, inline in ipairs(inlines) do if inline._type == "Quoted" or inline._type == "Formatted" or - inline._type == "Div" then + inline._type == "Div" or inline._type == "CiteInline" then self:move_punctuation(inline.inlines) end end @@ -1245,9 +1453,9 @@ end function OutputFormat:write_inline(inline, context) if inline.value then - return self:write_escaped(inline.value) + return self:write_escaped(inline.value, context) elseif inline.inlines then - return self:write_inlines(inline.inlines) + return self:write_inlines(inline.inlines, context) end return "" end @@ -1256,17 +1464,16 @@ function OutputFormat:write_escaped(str, context) return str end -function OutputFormat:write_link(inline, context) - return self:write_escaped(inline.value) -end - - local Markup = OutputFormat:new() function Markup:write_inline(inline, context) - -- Should be deprecated code + -- if not context then + -- print(debug.traceback()) + -- error('stop') + -- end if type(inline) == "string" then + -- Should not happen return self:write_escaped(inline, context) elseif type(inline) == "table" then @@ -1275,7 +1482,7 @@ function Markup:write_inline(inline, context) return self:write_escaped(inline.value, context) elseif inline._type == "InlineElement" then - return self:write_children(inline, context) + return self:write_inlines(inline.inlines, context) elseif inline._type == "Formatted" then return self:write_formatted(inline, context) @@ -1289,35 +1496,37 @@ function Markup:write_inline(inline, context) elseif inline._type == "Linked" then return self:write_link(inline, context) + elseif inline._type == "CiteInline" then + return self:write_cite(inline, context) + elseif inline._type == "Code" then - return self:write_code(inline.inlines) + return self:write_code(inline, context) elseif inline._type == "MathML" then - return self:write_mathml(inline.inlines) + return self:write_mathml(inline, context) elseif inline._type == "MathTeX" then - return self:write_math_tex(inline.inlines) + return self:write_math_tex(inline, context) - elseif inline._type == "NoCase" or inline._type == "NoDecor" then - return self:write_inlines(inline.inlines) + elseif inline._type == "NoCase" then + return self:write_nocase(inline, context) + + elseif inline._type == "NoDecor" then + return self:write_nodecor(inline, context) else - return self:write_inlines(inline.inlines) + return self:write_inlines(inline.inlines, context) end end return "" end -function Markup:write_children(inline, context) - local res = "" - for _, child_inline in ipairs(inline.inlines) do - res = res .. self:write_inline(child_inline, context) - end - return res +function Markup:write_formatted(inline, context) + return self:write_inlines(inline.inlines, context) end function Markup:write_quoted(inline, context) - local res = self:write_children(inline, context) + local res = self:write_inlines(inline.inlines, context) local quotes = inline.quotes if inline.is_inner then return quotes.inner_open .. res .. quotes.inner_close @@ -1326,6 +1535,26 @@ function Markup:write_quoted(inline, context) end end +function Markup:write_display(inline, context) + return self:write_inlines(inline.inlines, context) +end + +function Markup:write_link(inline, context) + return self:write_escaped(inline.value, context) +end + +function Markup:write_cite(inline, context) + return self:write_inlines(inline.inlines, context) +end + +function Markup:write_nocase(inline, context) + return self:write_inlines(inline.inlines, context) +end + +function Markup:write_nodecor(inline, context) + return self:write_inlines(inline.inlines, context) +end + local LatexWriter = Markup:new() @@ -1383,7 +1612,7 @@ function LatexWriter:write_escaped(str, context) end function LatexWriter:write_formatted(inline, context) - local res = self:write_children(inline, context) + local res = self:write_inlines(inline.inlines, context) for _, key in ipairs({"font-style", "font-variant", "font-weight", "text-decoration", "vertical-align"}) do local value = inline.formatting[key] if value then @@ -1400,7 +1629,7 @@ end function LatexWriter:write_display(inline, context) if inline.div == "left-margin" then local plainter_text_writer = output_module.PlainTextWriter:new() - local str = plainter_text_writer:write_inline(inline) + local str = plainter_text_writer:write_inline(inline, context) local len = utf8.len(str) if len > context.engine.registry.maxoffset then context.engine.registry.maxoffset = len @@ -1408,7 +1637,7 @@ function LatexWriter:write_display(inline, context) end end - local res = self:write_children(inline, context) + local res = self:write_inlines(inline.inlines, context) if inline.div == "left-margin" then if string.match(res, "%]") then res = "{" .. res .. "}" @@ -1435,6 +1664,14 @@ function LatexWriter:write_link(inline, context) end end +function LatexWriter:write_cite(inline, context) + local str = self:write_inlines(inline.inlines, context) + if context.engine.opt.citation_link then + str = string.format("\\cslcite{%s}{%s}", inline.cite_item.id, str) + end + return str +end + function LatexWriter:write_code(inline, context) return inline.value end @@ -1486,7 +1723,7 @@ function HtmlWriter:write_escaped(str, context) end function HtmlWriter:write_formatted(inline, context) - local res = self:write_children(inline, context) + local res = self:write_inlines(inline.inlines, context) for _, key in ipairs({"font-style", "font-variant", "font-weight", "text-decoration", "vertical-align"}) do local value = inline.formatting[key] if value then @@ -1503,7 +1740,7 @@ end function HtmlWriter:write_display(inline, context) if inline.div == "left-margin" then local plainter_text_writer = output_module.PlainTextWriter:new() - local str = plainter_text_writer:write_inline(inline) + local str = plainter_text_writer:write_inline(inline, context) local len = utf8.len(str) if len > context.engine.registry.maxoffset then context.engine.registry.maxoffset = len @@ -1514,7 +1751,7 @@ function HtmlWriter:write_display(inline, context) if #inline.inlines == 0 then return "" end - local res = self:write_children(inline, context) + local res = self:write_inlines(inline.inlines, context) local key = string.format("@display/%s", inline.div) if inline.div == "right-inline" then -- Strip trailing spaces @@ -1527,6 +1764,9 @@ function HtmlWriter:write_display(inline, context) end function HtmlWriter:write_link(inline, context) + -- if not context then + -- print(debug.traceback()) + -- end local content = self:write_escaped(inline.value, context) if context.engine.opt.wrap_url_and_doi then local href = self:write_escaped(inline.href, context) @@ -1558,14 +1798,15 @@ function PlainTextWriter:write_escaped(str, context) end function PlainTextWriter:write_formatted(inline, context) - return self:write_children(inline, context) + return self:write_inlines(inline.inlines, context) end function PlainTextWriter:write_display(inline, context) - return self:write_children(inline, context) + return self:write_inlines(inline.inlines, context) end +-- Omit formatting and quotes local SortStringFormat = OutputFormat:new() function SortStringFormat:output(inlines, context) @@ -1575,11 +1816,13 @@ function SortStringFormat:output(inlines, context) end function SortStringFormat:write_escaped(str, context) + -- sort_Quotes.txt str = string.gsub(str, ",", "") return str end +-- Omit formatting and quotes local DisamStringFormat = OutputFormat:new() function DisamStringFormat:output(inlines, context) @@ -1625,10 +1868,42 @@ function DisamStringFormat:flatten_seq_ir(ir) -- assert ir.quotes == localized quotes inlines = self:affixed_quoted(inlines, ir.affixes, ir.quotes); inlines = self:with_display(inlines, ir.display); + + -- -- A citation layout + -- if ir._element == "layout" and ir.cite_item then + -- inlines = {CiteInline:new(inlines, ir.cite_item)} + -- end + return inlines end +local PseudoHtml = HtmlWriter:new() + +function PseudoHtml :write_escaped(str, context) + -- str = string.gsub(str, "%&", "&") + -- str = string.gsub(str, "<", "<") + -- str = string.gsub(str, ">", ">") + -- for char, sub in pairs(util.superscripts) do + -- str = string.gsub(str, char, "<sup>" .. sub .. "</sup>") + -- end + return str +end + +function PseudoHtml:write_mathml(inline, context) + return string.format('<math>%s</math>', inline.value) +end + +function PseudoHtml:write_math_tex(inline, context) + return string.format("<math>%s</math>", inline.value) +end + +function PseudoHtml:write_nocase(inline, context) + local str = self:write_inlines(inline.inlines, context) + return string.format('<span class="nocase">%s</span>', str) +end + + function InlineElement.has_space(inlines) local str = DisamStringFormat:output(inlines) if not str then @@ -1711,6 +1986,10 @@ output_module.Micro = Micro output_module.Quoted = Quoted output_module.Linked = Linked output_module.Div = Div +output_module.CiteInline = CiteInline +output_module.Code = Code +output_module.MathTeX = MathTeX +output_module.MathTML = MathML output_module.NoCase = NoCase output_module.NoDecor = NoDecor @@ -1722,5 +2001,6 @@ output_module.HtmlWriter = HtmlWriter output_module.PlainTextWriter = PlainTextWriter output_module.DisamStringFormat = DisamStringFormat output_module.SortStringFormat = SortStringFormat +output_module.PseudoHtml = PseudoHtml return output_module |