summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/typo-brk.lua
diff options
context:
space:
mode:
authorContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
commit8d8d528d2ad52599f11250cfc567fea4f37f2a8b (patch)
tree94286bc131ef7d994f9432febaf03fe23d10eef8 /tex/context/base/mkiv/typo-brk.lua
parentf5aed2e51223c36c84c5f25a6cad238b2af59087 (diff)
downloadcontext-8d8d528d2ad52599f11250cfc567fea4f37f2a8b.tar.gz
2016-01-12 16:26:00
Diffstat (limited to 'tex/context/base/mkiv/typo-brk.lua')
-rw-r--r--tex/context/base/mkiv/typo-brk.lua359
1 files changed, 359 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/typo-brk.lua b/tex/context/base/mkiv/typo-brk.lua
new file mode 100644
index 000000000..146694494
--- /dev/null
+++ b/tex/context/base/mkiv/typo-brk.lua
@@ -0,0 +1,359 @@
+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 = next, type, tonumber
+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 tonode = nuts.tonode
+
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getboth = nuts.getboth
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+local getfont = nuts.getfont
+local getid = nuts.getid
+local getfield = nuts.getfield
+local getattr = nuts.getattr
+
+local setfield = nuts.setfield
+local setattr = nuts.setattr
+local setlink = nuts.setlink
+local setchar = nuts.setchar
+local setdisc = nuts.setdisc
+
+local copy_node = nuts.copy
+local copy_nodelist = nuts.copy_list
+local free_node = nuts.free
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local remove_node = nuts.remove
+
+local tonodes = nuts.tonodes
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local nodepool = nuts.pool
+local tasks = nodes.tasks
+
+local v_reset = interfaces.variables.reset
+
+local implement = interfaces.implement
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+local new_disc = nodepool.disc
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+
+local kerning_code = kerncodes.kerning
+
+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
+
+local function insert_break(head,start,before,after)
+ insert_node_before(head,start,new_penalty(before))
+ insert_node_before(head,start,new_glue(0))
+ insert_node_after(head,start,new_glue(0))
+ insert_node_after(head,start,new_penalty(after))
+end
+
+methods[1] = function(head,start)
+ local p, n = getboth(start)
+ if p and n then
+ insert_break(head,start,10000,0)
+ end
+ return head, start
+end
+
+methods[2] = 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())
+ -- setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) -- just a copy will do
+ setfield(start,"attr",getfield(tmp,"attr"))
+ setfield(start,"replace",tmp)
+ local tmp = copy_node(tmp)
+ local hyphen = copy_node(tmp)
+ setchar(hyphen,languages.prehyphenchar(getfield(tmp,"lang")))
+ setlink(tmp,hyphen)
+ setfield(start,"post",tmp)
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[3] = 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())
+ -- setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) -- just a copy will do
+ setfield(start,"attr",getfield(tmp,"attr"))
+ setfield(start,"replace",tmp)
+ local tmp = copy_node(tmp)
+ local hyphen = copy_node(tmp)
+ setchar(hyphen,languages.prehyphenchar(getfield(tmp,"lang")))
+ setlink(hyphen,tmp)
+ setfield(start,"pre",hyphen)
+ insert_break(head,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())
+ -- setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) -- just a copy will do
+ setfield(start,"attr",getfield(tmp,"attr"))
+ setdisc(start,copy_node(tmp),copy_node(tmp),tmp)
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[5] = function(head,start,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 = getfield(tmp,"attr")
+ 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)
+ -- setfield(start,"attr",copy_nodelist(attr)) -- todo: critical only -- just a copy will do
+ setfield(start,"attr",attr) -- todo: critical only -- just a copy will do
+ free_node(tmp)
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+function breakpoints.handler(head)
+ head = tonut(head)
+ local done, numbers = false, languages.numbers
+ local start, n = head, 0
+ while start do
+ local id = getid(start)
+ if id == glyph_code then
+ local attr = getattr(start,a_breakpoints)
+ if attr and attr > 0 then
+ setattr(start,a_breakpoints,unsetvalue) -- maybe test for subtype > 256 (faster)
+ -- look ahead and back n chars
+ local data = mapping[attr]
+ if data then
+ local map = data.characters
+ local cmap = map[getchar(start)]
+ if cmap then
+ local lang = getfield(start,"lang")
+ -- we do a sanity check for language
+ local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])
+ if smap then
+ if n >= smap.nleft then
+ local m = smap.nright
+ local next = getnext(start)
+ while next do -- gamble on same attribute (not that important actually)
+ local id = getid(next)
+ if id == glyph_code then -- gamble on same attribute (not that important actually)
+ if map[getchar(next)] then
+ break
+ elseif m == 1 then
+ local method = methods[smap.type]
+ if method then
+ head, start = method(head,start,smap)
+ done = true
+ end
+ break
+ else
+ m = m - 1
+ next = getnext(next)
+ end
+ elseif id == kern_code and getsubtype(next) == kerning_code then
+ next = getnext(next)
+ -- ignore intercharacter kerning, will go way
+ else
+ -- we can do clever and set n and jump ahead but ... not now
+ break
+ end
+ end
+ end
+ n = 0
+ else
+ n = n + 1
+ end
+ else
+ n = n + 1
+ end
+ else
+ n = 0
+ end
+ else
+ -- n = n + 1 -- if we want single char handling (|-|) then we will use grouping and then we need this
+ end
+ elseif id == kern_code and getsubtype(start) == kerning_code then
+ -- ignore intercharacter kerning, will go way
+ else
+ n = 0
+ end
+ start = getnext(start)
+ end
+ return tonode(head), done
+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,
+ } -- 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
+ tasks.enableaction("processors","typesetters.breakpoints.handler")
+ end
+ n = n.number
+ end
+ end
+ texsetattribute(a_breakpoints,n)
+end
+
+-- function breakpoints.enable()
+-- tasks.enableaction("processors","typesetters.breakpoints.handler")
+-- end
+
+-- interface
+
+implement {
+ name = "definebreakpoints",
+ actions = breakpoints.define,
+ arguments = "string"
+}
+
+implement {
+ name = "definebreakpoint",
+ actions = breakpoints.setreplacement,
+ arguments = {
+ "string",
+ "string",
+ "string",
+ {
+ { "type", "integer" },
+ { "nleft" },
+ { "nright" },
+ { "right" },
+ { "left" },
+ { "middle" },
+ }
+ }
+}
+
+implement {
+ name = "setbreakpoints",
+ actions = breakpoints.set,
+ arguments = "string"
+}