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 settings_to_array = aux.settings_to_array local has_attribute = node.has_attribute local unset_attribute = node.unset_attribute local set_attribute = node.set_attribute local copy_node = node.copy local free_node = node.free local insert_node_before = node.insert_before local insert_node_after = node.insert_after local make_penalty_node = nodes.penalty local make_glue_node = nodes.glue local make_disc_node = nodes.disc local make_glyph_node = nodes.glyph local remove_node = nodes.remove -- ! nodes local tonodes = blobs.tonodes local glyph = node.id("glyph") local kern = node.id("kern") breakpoints = breakpoints or { } breakpoints.mapping = breakpoints.mapping or { } breakpoints.methods = breakpoints.methods or { } breakpoints.attribute = attributes.private("breakpoint") storage.register("breakpoints/mapping", breakpoints.mapping, "breakpoints.mapping") local mapping = breakpoints.mapping function breakpoints.setreplacement(id,char,language,settings) char = utfbyte(char) local map = mapping[id] if not map then map = { } mapping[id] = map end local cmap = map[char] if not cmap then cmap = { } map[char] = cmap end local left, right, middle = settings.left, settings.right, settings.middle cmap[language or ""] = { kind = tonumber(settings.kind) 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 { kind or 1, before or 1, after or 1 } end local function insert_break(head,start,before,after) insert_node_before(head,start,make_penalty_node(before)) insert_node_before(head,start,make_glue_node(0)) insert_node_after(head,start,make_glue_node(0)) insert_node_after(head,start,make_penalty_node(after)) end breakpoints.methods[1] = function(head,start) if start.prev and start.next then insert_break(head,start,10000,0) end return head, start end breakpoints.methods[2] = function(head,start) -- ( => (- if start.prev and start.next then local tmp head, start, tmp = remove_node(head,start) head, start = insert_node_before(head,start,make_disc_node()) start.replace = tmp local tmp, hyphen = copy_node(tmp), copy_node(tmp) hyphen.char = languages.prehyphenchar(tmp.lang) tmp.next, hyphen.prev = hyphen, tmp start.post = tmp insert_break(head,start,10000,10000) end return head, start end breakpoints.methods[3] = function(head,start) -- ) => -) if start.prev and start.next then local tmp head, start, tmp = remove_node(head,start) head, start = insert_node_before(head,start,make_disc_node()) start.replace = tmp local tmp, hyphen = copy_node(tmp), copy_node(tmp) hyphen.char = languages.prehyphenchar(tmp.lang) tmp.prev, hyphen.next = hyphen, tmp start.pre = hyphen insert_break(head,start,10000,10000) end return head, start end breakpoints.methods[4] = function(head,start) -- - => - - - if start.prev and start.next then local tmp head, start, tmp = remove_node(head,start) head, start = insert_node_before(head,start,make_disc_node()) start.pre, start.post, start.replace = copy_node(tmp), copy_node(tmp), tmp insert_break(head,start,10000,10000) end return head, start end breakpoints.methods[5] = function(head,start,settings) -- x => p q r if start.prev and start.next then local tmp head, start, tmp = remove_node(head,start) head, start = insert_node_before(head,start,make_disc_node()) start.pre, start.post, start.replace = tonodes(settings.right,tmp), tonodes(settings.left,tmp), tonodes(settings.middle,tmp) free_node(tmp) insert_break(head,start,10000,10000) end return head, start end local methods = breakpoints.methods function breakpoints.process(namespace,attribute,head) local done, numbers = false, languages.numbers local start, n = head, 0 while start do local id = start.id if id == glyph then local attr = has_attribute(start,attribute) if attr and attr > 0 then unset_attribute(start,attribute) -- maybe test for subtype > 256 (faster) -- look ahead and back n chars local map = mapping[attr] if map then local cmap = map[start.char] if cmap then local lang = 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 = start.next while next do -- gamble on same attribute (not that important actually) local id = next.id if id == glyph then -- gamble on same attribute (not that important actually) if map[next.char] then break elseif m == 1 then local method = methods[smap.kind] if method then head, start = method(head,start,smap) done = true end break else m = m - 1 next = next.next end elseif id == kern and next.subtype == 0 then next = next.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 and start.subtype == 0 then -- ignore intercharacter kerning, will go way else n = 0 end start = start.next end return head, done end chars.handle_breakpoints = nodes.install_attribute_handler { name = "breakpoint", namespace = breakpoints, processor = breakpoints.process, } function breakpoints.enable() tasks.enableaction("processors","chars.handle_breakpoints") end