summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-output.lua
diff options
context:
space:
mode:
Diffstat (limited to 'biblio/citation-style-language/citeproc-output.lua')
-rw-r--r--biblio/citation-style-language/citeproc-output.lua482
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, "<", "&#60;")
+ -- str = string.gsub(str, ">", "&#62;")
+ -- 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