summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua')
-rw-r--r--Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua305
1 files changed, 305 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua b/Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua
new file mode 100644
index 00000000000..6af13714f00
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/polyglossia/polyglossia-punct.lua
@@ -0,0 +1,305 @@
+require('polyglossia') -- just in case...
+
+local add_to_callback = luatexbase.add_to_callback
+local remove_from_callback = luatexbase.remove_from_callback
+local priority_in_callback = luatexbase.priority_in_callback
+local new_attribute = luatexbase.new_attribute
+
+local node = node
+
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local remove_node = node.remove
+local has_attribute = node.has_attribute
+local node_copy = node.copy
+local new_node = node.new
+local end_of_math = node.end_of_math
+local getnext = node.getnext
+local getprev = node.getprev
+
+-- node types according to node.types()
+local glue_code = node.id"glue"
+local glyph_code = node.id"glyph"
+local penalty_code = node.id"penalty"
+local kern_code = node.id"kern"
+local math_code = node.id"math"
+
+-- we need some node subtypes
+local userkern = 1
+local userskip = 0
+local removable_skip = {
+ [0] = true, -- userskip
+ [13] = true, -- spaceskip
+ [14] = true, -- xspaceskip
+}
+
+-- we make a new node, so that we can copy it later on
+local kern_node = new_node(kern_code)
+kern_node.subtype = userkern -- this kern can be removed later on
+
+local function get_kern_node(dim)
+ local n = node_copy(kern_node)
+ n.kern = dim
+ return n
+end
+
+local glue_node = new_node(glue_code)
+glue_node.subtype = userskip
+
+local function get_glue_node(dim, stretch, shrink)
+ local n = node_copy(glue_node)
+ n.width = dim
+ n.stretch = stretch
+ n.shrink = shrink
+ return n
+end
+
+local penalty_node = new_node(penalty_code)
+penalty_node.penalty = 10000
+
+local function get_penalty_node()
+ return node_copy(penalty_node)
+end
+
+-- we have here all possible space characters, referenced by their unicode slot
+-- number, taken from char-def.lua
+local space_chars = {[32] = true, [160] = true, [5760] = true, [6158] = true,
+ [8192] = true, [8193] = true, [8194] = true, [8195] = true, [8196] = true,
+ [8197] = true, [8198] = true, [8199] = true, [8200] = true, [8201] = true,
+ [8202] = true, [8239] = true, [8287] = true, [12288] = true}
+
+-- from nodes-tst.lua, adapted
+local function somespace(n)
+ if n then
+ local id, subtype = n.id, n.subtype
+ if id == glue_code then
+ -- it is dangerous to remove all the type of glue
+ return removable_skip[subtype]
+ elseif id == kern_code then
+ -- remove only user's kern
+ return subtype == userkern
+ elseif id == glyph_code then
+ return space_chars[n.char]
+ end
+ end
+end
+
+-- we have here all left bracket characters, referenced by their unicode slot
+local left_bracket_chars = {[40] = true, [123] = true, [91] = true, [10216] = true}
+
+local function someleftbracket(n)
+ if n then
+ local id = n.id
+ if id == glyph_code then
+ return left_bracket_chars[n.char]
+ end
+ end
+end
+
+-- we have here all right bracket characters, referenced by their unicode slot
+local right_bracket_chars = {[41] = true, [125] = true, [93] = true, [10217] = true}
+
+local function somerightbracket(n)
+ if n then
+ local id = n.id
+ if id == glyph_code then
+ return right_bracket_chars[n.char]
+ end
+ end
+end
+
+-- idem
+local function somepenalty(n, value)
+ if n then
+ local id = n.id
+ if id == penalty_code then
+ if value then
+ return n.penalty == value
+ else
+ return true
+ end
+ end
+ end
+end
+
+local punct_attr = new_attribute("polyglossia_punct")
+
+local lang_id = {}
+local lang_counter = 0
+local left_space = {}
+local right_space = {}
+
+local function ensure_lang_id(lang)
+ if not lang_id[lang] then
+ lang_counter = lang_counter + 1
+ lang_id[lang] = lang_counter
+ end
+ return lang_id[lang]
+end
+
+local function clear_spaced_characters(lang)
+ local id = ensure_lang_id(lang)
+ left_space[id] = {}
+ right_space[id] = {}
+end
+
+local function illegal_unit(unit)
+ if unit then
+ texio.write_nl('Illegal spacing unit "'..unit..'".')
+ else
+ texio.write_nl('Spacing unit is a nil value.')
+ end
+end
+
+local function add_left_spaced_character(lang, char, kern, unit, rubber)
+-- The parameter kern is a number meant as a fraction of the unit.
+-- The unit can be "quad" (1em) or "space" (interword space).
+-- The parameter rubber is a Boolean value indicating if the inserted space is
+-- stretchable and shrinkable (only relevant if the unit is "space").
+ local id = ensure_lang_id(lang)
+ if unit == "quad" or unit == "space" then
+ left_space[id][char] = {}
+ left_space[id][char]["kern"] = kern
+ left_space[id][char]["unit"] = unit
+ left_space[id][char]["rubber"] = rubber
+ else
+ illegal_unit(unit)
+ end
+end
+
+local function add_right_spaced_character(lang, char, kern, unit, rubber)
+ local id = ensure_lang_id(lang)
+ if unit == "quad" or unit == "space" then
+ right_space[id][char] = {}
+ right_space[id][char]["kern"] = kern
+ right_space[id][char]["unit"] = unit
+ right_space[id][char]["rubber"] = rubber
+ else
+ illegal_unit(unit)
+ end
+end
+
+-- from typo-spa.lua, adapted
+local function process(head)
+ local current = head
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ local attr = has_attribute(current, punct_attr)
+ if attr then
+ local char = utf8.char(current.char) -- requires Lua 5.3
+ local leftspace = left_space[attr][char]
+ local rightspace = right_space[attr][char]
+ if leftspace or rightspace then
+ local fontparameters = fonts.hashes.parameters[current.font]
+ local unit, stretch, shrink, spacing_node
+ if leftspace then
+ local prev = getprev(current)
+ local space_exception = false
+ if prev then
+ local prevprev = getprev(prev)
+ -- do not add space after left (opening) bracket
+ space_exception = someleftbracket(prev)
+ if somespace(prev) then
+ -- TODO: there is a question here: do we override a preceding space or not?...
+ if somepenalty(prevprev, 10000) then
+ head = remove_node(head, prevprev)
+ end
+ head = remove_node(head, prev)
+ end
+ end
+ if leftspace.unit == "quad" then
+ unit = fontparameters.quad
+ spacing_node = get_kern_node(leftspace.kern*unit)
+ elseif leftspace.unit == "space" then
+ unit = fontparameters.space
+ if leftspace.rubber then
+ stretch = leftspace.kern*fontparameters.space_stretch
+ shrink = leftspace.kern*fontparameters.space_shrink
+ spacing_node = get_glue_node(leftspace.kern*unit, stretch, shrink)
+ head = insert_node_before(head, current, get_penalty_node())
+ else
+ spacing_node = get_kern_node(leftspace.kern*unit)
+ end
+ end
+ if not space_exception then
+ head = insert_node_before(head, current, spacing_node)
+ end
+ end
+ if rightspace then
+ local next = getnext(current)
+ local space_exception = false
+ if next then
+ -- do not add space before right (closing) bracket
+ space_exception = somerightbracket(next)
+ local nextnext = getnext(next)
+ if somepenalty(next, 10000) and somespace(nextnext) then
+ head = remove_node(head, next)
+ head = remove_node(head, nextnext)
+ elseif somespace(next) then
+ head = remove_node(head, next)
+ end
+ end
+ if rightspace.unit == "quad" then
+ unit = fontparameters.quad
+ spacing_node = get_kern_node(rightspace.kern*unit)
+ elseif rightspace.unit == "space" then
+ unit = fontparameters.space
+ if rightspace.rubber then
+ stretch = rightspace.kern*fontparameters.space_stretch
+ shrink = rightspace.kern*fontparameters.space_shrink
+ spacing_node = get_glue_node(rightspace.kern*unit, stretch, shrink)
+ if not space_exception then
+ head, current = insert_node_after(head, current, get_penalty_node())
+ end
+ else
+ spacing_node = get_kern_node(rightspace.kern*unit)
+ end
+ end
+ if not space_exception then
+ head, current = insert_node_after(head, current, spacing_node)
+ end
+ end
+ end
+ end
+ elseif id == math_code then
+ -- warning: this is a feature of luatex > 0.76
+ current = end_of_math(current) -- weird, can return nil .. no math end?
+ end
+ current = getnext(current) -- no error even if current is nil
+ end
+ return head
+end
+
+local function activate(lang)
+ local id = ensure_lang_id(lang)
+ -- We set the punctuation attribute to a language id here. This is
+ -- important to be able to intermix languages with different spacings
+ -- in one paragraph.
+ tex.setattribute(punct_attr, id)
+ for _, callback_name in ipairs{ "pre_linebreak_filter", "hpack_filter" } do
+ if not priority_in_callback(callback_name, "polyglossia-punct.process") then
+ add_to_callback(callback_name, process, "polyglossia-punct.process", 1)
+ end
+ end
+end
+
+local function deactivate()
+ tex.setattribute(punct_attr, -0x7FFFFFFF) -- this value means "unset"
+ -- Though it would make compilation slightly faster, it is not possible to
+ -- safely uncomment the following lines. Imagine the following case: you
+ -- start a paragraph by some spaced punctuation text, then, in the same
+ -- paragraph, you change the language to something else, and thus call the
+ -- following lines. This means that, at the end of the paragraph, the
+ -- function won't be in the callback, so the beginning of the paragraph
+ -- won't be processed by it.
+ -- if priority_in_callback(callback_name, "polyglossia-punct.process") then
+ -- remove_from_callback(callback_name, "polyglossia-punct.process")
+ -- end
+end
+
+polyglossia.activate_punct = activate
+polyglossia.deactivate_punct = deactivate
+polyglossia.add_left_spaced_character = add_left_spaced_character
+polyglossia.add_right_spaced_character = add_right_spaced_character
+polyglossia.clear_spaced_characters = clear_spaced_characters