diff options
Diffstat (limited to 'tex/context/base/mkiv/typo-brk.lua')
-rw-r--r-- | tex/context/base/mkiv/typo-brk.lua | 359 |
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" +} |