summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt498
1 files changed, 498 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt
new file mode 100644
index 00000000000..8d2fcd147b7
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkxl/typo-brk.lmt
@@ -0,0 +1,498 @@
+if not modules then modules = { } end modules ['typo-brk'] = {
+ version = 1.001,
+ comment = "companion to typo-brk.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this code dates from the beginning and is kind of experimental; it
+-- will be optimized and improved soon
+
+local next, type, tonumber, tostring = next, type, tonumber, tostring
+local utfbyte, utfchar = utf.byte, utf.char
+local format = string.format
+
+local trace_breakpoints = false trackers.register("typesetters.breakpoints", function(v) trace_breakpoints = v end)
+
+local report_breakpoints = logs.reporter("typesetting","breakpoints")
+
+local nodes, node = nodes, node
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getsubtype = nuts.getsubtype
+local getfont = nuts.getfont
+local getid = nuts.getid
+local getattrlist = nuts.getattrlist
+local getattr = nuts.getattr
+local getlanguage = nuts.getlanguage
+local isglyph = nuts.isglyph
+
+local setattr = nuts.setattr
+local setattrlist = nuts.setattrlist
+local setlink = nuts.setlink
+local setchar = nuts.setchar
+local setdisc = nuts.setdisc
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local setboth = nuts.setboth
+local setsubtype = nuts.setsubtype
+
+local copy_node = nuts.copy_node
+local copy_node_list = nuts.copy_list
+local flush_node = nuts.flush_node
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+local end_of_math = nuts.end_of_math
+local find_attribute = nuts.find_attribute
+local unset_attributes = nuts.unset_attributes
+
+local tonodes = nuts.tonodes
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local nodepool = nuts.pool
+local enableaction = nodes.tasks.enableaction
+
+local v_reset = interfaces.variables.reset
+local v_yes = interfaces.variables.yes
+
+local implement = interfaces.implement
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+local new_disc = nodepool.disc
+local new_wordboundary = nodepool.wordboundary
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local kern_code = nodecodes.kern
+local math_code = nodecodes.math
+
+local fontkern_code = kerncodes.fontkern
+local italickern_code = kerncodes.italiccorrection
+
+local is_letter = characters.is_letter
+
+local typesetters = typesetters
+
+typesetters.breakpoints = typesetters.breakpoints or {}
+local breakpoints = typesetters.breakpoints
+
+breakpoints.mapping = breakpoints.mapping or { }
+breakpoints.numbers = breakpoints.numbers or { }
+
+breakpoints.methods = breakpoints.methods or { }
+local methods = breakpoints.methods
+
+local a_breakpoints = attributes.private("breakpoint")
+
+storage.register("typesetters/breakpoints/mapping", breakpoints.mapping, "typesetters.breakpoints.mapping")
+
+local mapping = breakpoints.mapping
+local numbers = breakpoints.mapping
+
+for i=1,#mapping do
+ local m = mapping[i]
+ numbers[m.name] = m
+end
+
+-- this needs a cleanup ... maybe make all of them disc nodes
+
+-- todo: use boundaries
+
+local function insert_break(head,start,stop,before,after,kern)
+ if not kern then
+ local p = new_penalty(before)
+ local g = new_glue()
+ setattrlist(p,start)
+ setattrlist(g,start)
+ insert_node_before(head,start,p)
+ insert_node_before(head,start,g)
+ end
+ local p = new_penalty(after)
+ local g = new_glue()
+ setattrlist(p,start)
+ setattrlist(g,start)
+ insert_node_after(head,stop,g)
+ insert_node_after(head,stop,p)
+end
+
+methods[1] = function(head,start,stop,settings,kern)
+ local p, n = getboth(stop)
+ if p and n then
+ insert_break(head,start,stop,10000,0,kern)
+ end
+ return head, stop
+end
+
+methods[6] = function(head,start,stop,settings,kern)
+ local p = getprev(start)
+ local n = getnext(stop)
+ if p and n then
+ if kern then
+ insert_break(head,start,stop,10000,0,kern)
+ else
+ local l = new_wordboundary()
+ local d = new_disc()
+ local r = new_wordboundary()
+ setattrlist(d,start) -- otherwise basemode is forced and we crash
+ setlink(p,l,d,r,n)
+ if start == stop then
+ setboth(start)
+ setdisc(d,start,nil,copy_node(start))
+ else
+ setprev(start)
+ setnext(stop)
+ setdisc(d,start,nil,copy_node_list(start))
+ end
+ stop = r
+ end
+ end
+ return head, stop
+end
+
+methods[2] = function(head,start) -- ( => (-
+ local p, n = getboth(start)
+ if p and n then
+ local replace
+ head, start, replace = remove_node(head,start)
+ local post = copy_node(replace)
+ local hyphen = copy_node(post)
+ setchar(hyphen,languages.prehyphenchar(getlanguage(post)))
+ setlink(post,hyphen)
+ head, start = insert_node_before(head,start,new_disc(nil,post,replace))
+ setattrlist(start,replace)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[3] = function(head,start) -- ) => -)
+ local p, n = getboth(start)
+ if p and n then
+ local replace
+ head, start, replace = remove_node(head,start)
+ local pre = copy_node(replace)
+ local hyphen = copy_node(pre)
+ setchar(hyphen,languages.prehyphenchar(getlanguage(pre)))
+ setlink(hyphen,pre)
+ head, start = insert_node_before(head,start,new_disc(hyphen,nil,replace)) -- so not pre !
+ setattrlist(start,tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[4] = function(head,start) -- - => - - -
+ local p, n = getboth(start)
+ if p and n then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ setattrlist(start,tmp)
+ setdisc(start,copy_node(tmp),copy_node(tmp),tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[5] = function(head,start,stop,settings) -- x => p q r
+ local p, n = getboth(start)
+ if p and n then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ local attr = getattrlist(tmp)
+ local font = getfont(tmp)
+ local left = settings.left
+ local right = settings.right
+ local middle = settings.middle
+ if left then
+ left = tonodes(tostring(left),font,attr)
+ end
+ if right then
+ right = tonodes(tostring(right),font,attr)
+ end
+ if middle then
+ middle = tonodes(tostring(middle),font,attr)
+ end
+ setdisc(start,left,right,middle)
+ setattrlist(start,attr)
+ flush_node(tmp)
+ insert_break(head,start,start,10000,10000)
+ end
+ return head, start
+end
+
+-- we know we have a limited set
+-- what if characters are replaced by the font handler
+-- do we need to go into disc nodes (or do it as first step but then we need a pre/post font handler)
+
+function breakpoints.handler(head)
+ local _, current = find_attribute(head, a_breakpoints)
+ if current then
+ local done = false
+ local attr = nil
+ local map = nil
+ local current = head
+ while current do
+ local char, id = isglyph(current)
+ if char then
+ local a = getattr(current,a_breakpoints)
+ if a and a > 0 then
+ if a ~= attr then
+ local data = mapping[a]
+ if data then
+ map = data.characters
+ else
+ map = nil
+ end
+ attr = a
+ end
+ if map then
+ local cmap = map[char]
+ if cmap then
+ -- for now we collect but when found ok we can move the handler here
+ -- although it saves nothing in terms of performance
+ local lang = getlanguage(current)
+ local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[languages.numbers[lang]] or cmap[""])
+ if smap then
+ local skip = smap.skip
+ local start = current
+ local stop = current
+ current = getnext(current)
+ if skip then
+ while current do
+ local c = isglyph(current)
+ if c == char then
+ stop = current
+ current = getnext(current)
+ else
+ break
+ end
+ end
+ end
+ local d = { start, stop, cmap, smap, char }
+ if done then
+ done[#done+1] = d
+ else
+ done = { d }
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ elseif id == math_code then
+ attr = nil
+ current = end_of_math(current)
+ if current then
+ current = getnext(current)
+ end
+ else
+ current = getnext(current)
+ end
+ end
+ if not done then
+ return head
+ end
+ -- we have hits
+ -- local numbers = languages.numbers
+ for i=1,#done do
+ local data = done[i]
+ local start = data[1]
+ local stop = data[2]
+ local cmap = data[3]
+ local smap = data[4]
+ -- we do a sanity check for language
+ -- local lang = getlanguage(start)
+ -- local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])
+ -- if smap then
+ local nleft = smap.nleft
+ local cleft = 0
+ local prev = getprev(start)
+ local kern = nil
+ while prev and nleft ~= cleft do
+ local char, id = isglyph(prev)
+ if char then
+ if not is_letter[char] then
+ cleft = -1
+ break
+ end
+ cleft = cleft + 1
+ prev = getprev(prev)
+ elseif id == kern_code then
+ local s = getsubtype(prev)
+ if s == fontkern_code or s == italickern_code then
+ if cleft == 0 then
+ kern = prev
+ prev = getprev(prev)
+ else
+ break
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if nleft == cleft then
+ local nright = smap.nright
+ local cright = 0
+ local next = getnext(stop) -- getnext(start)
+ while next and nright ~= cright do
+ local char, id = isglyph(next)
+ if char then
+ if not is_letter[char] then
+ cright = -1
+ break
+ end
+ if cright == 1 and cmap[char] then
+ -- let's not make it too messy
+ break
+ end
+ cright = cright + 1
+ next = getnext(next)
+ elseif id == kern_code then
+ local s = getsubtype(next)
+ if s == fontkern_code or s == italickern_code then
+ if cleft == 0 then
+ next = getnext(next)
+ else
+ break
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if nright == cright then
+ local method = methods[smap.type]
+ if method then
+ head, start = method(head,start,stop,smap,kern)
+ end
+ end
+ -- end
+ end
+ end
+ if done then
+-- unset_attributes(a_breakpoints, head)
+ end
+ end
+ return head
+end
+
+local enabled = false
+
+function breakpoints.define(name)
+ local data = numbers[name]
+ if data then
+ -- error
+ else
+ local number = #mapping + 1
+ local data = {
+ name = name,
+ number = number,
+ characters = { },
+ }
+ mapping[number] = data
+ numbers[name] = data
+ end
+end
+
+function breakpoints.setreplacement(name,char,language,settings)
+ char = utfbyte(char)
+ local data = numbers[name]
+ if data then
+ local characters = data.characters
+ local cmap = characters[char]
+ if not cmap then
+ cmap = { }
+ characters[char] = cmap
+ end
+ local left, right, middle = settings.left, settings.right, settings.middle
+ cmap[language or ""] = {
+ type = tonumber(settings.type) or 1,
+ nleft = tonumber(settings.nleft) or 1,
+ nright = tonumber(settings.nright) or 1,
+ left = left ~= "" and left or nil,
+ right = right ~= "" and right or nil,
+ middle = middle ~= "" and middle or nil,
+ skip = settings.range == v_yes,
+ } -- was { type or 1, before or 1, after or 1 }
+ end
+end
+
+function breakpoints.set(n)
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = mapping[n]
+ if not n then
+ n = unsetvalue
+ else
+ if not enabled then
+ if trace_breakpoints then
+ report_breakpoints("enabling breakpoints handler")
+ end
+ enableaction("processors","typesetters.breakpoints.handler")
+ end
+ n = n.number
+ end
+ end
+ texsetattribute(a_breakpoints,n)
+end
+
+-- interface
+
+implement {
+ name = "definebreakpoints",
+ actions = breakpoints.define,
+ arguments = "string"
+}
+
+implement {
+ name = "definebreakpoint",
+ actions = breakpoints.setreplacement,
+ arguments = {
+ "string",
+ "string",
+ "string",
+ {
+ { "type", "integer" },
+ { "nleft", "integer" },
+ { "nright", "integer" },
+ { "right" },
+ { "left" },
+ { "middle" },
+ { "range" },
+ }
+ }
+}
+
+implement {
+ name = "setbreakpoints",
+ actions = breakpoints.set,
+ arguments = "string"
+}