diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt new file mode 100644 index 00000000000..fa507df4b1a --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkxl/typo-fln.lmt @@ -0,0 +1,406 @@ +if not modules then modules = { } end modules ['typo-fln'] = { + version = 1.001, + comment = "companion to typo-fln.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- When I ran into the following experimental code again, I figured that it dated +-- from the early days of mkiv, so I updates it a bit to fit into todays context. +-- In the process I might have messed up things. For instance we had a diffent +-- wrapper then using head and tail. + +-- todo: only letters (no punctuation) +-- todo: nuts + +local trace_firstlines = false trackers.register("typesetters.firstlines", function(v) trace_firstlines = v end) +local report_firstlines = logs.reporter("nodes","firstlines") + +typesetters.firstlines = typesetters.firstlines or { } +local firstlines = typesetters.firstlines + +local nodes = nodes + +local tasks = nodes.tasks +local enableaction = tasks.enableaction +local disableaction = tasks.disableaction + +local context = context +local implement = interfaces.implement + +local nuts = nodes.nuts +local tonode = nuts.tonode + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getboth = nuts.getboth +local setboth = nuts.setboth +local getid = nuts.getid +local getwidth = nuts.getwidth +local getlist = nuts.getlist +local setlist = nuts.setlist +local getattr = nuts.getattr +local getbox = nuts.getbox +local getdisc = nuts.getdisc +local setdisc = nuts.setdisc +local setlink = nuts.setlink +local setfont = nuts.setfont +local setglyphdata = nuts.setglyphdata +local getprop = nuts.getprop +local setprop = nuts.setprop + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local par_code = nodecodes.par + +local spaceskip_code = nodes.gluecodes.spaceskip + +local nextglyph = nuts.traversers.glyph +local nextdisc = nuts.traversers.disc + +local flush_node_list = nuts.flush_list +local flush_node = nuts.flush_node +local copy_node_list = nuts.copy_list +local insert_node_before = nuts.insert_before +local insert_node_after = nuts.insert_after +local remove_node = nuts.remove +local getdimensions = nuts.dimensions +local hpack_node_list = nuts.hpack +local start_of_par = nuts.start_of_par + +local setcoloring = nuts.colors.set + +local nodepool = nuts.pool +local newpenalty = nodepool.penalty +local newkern = nodepool.kern +local tracerrule = nodes.tracers.pool.nuts.rule + +local actions = { } +firstlines.actions = actions + +local a_firstline = attributes.private('firstline') + +local texget = tex.get + +local variables = interfaces.variables +local v_default = variables.default +local v_line = variables.line +local v_word = variables.word + +local function set(par,specification) + enableaction("processors","typesetters.firstlines.handler") + if trace_firstlines then + report_firstlines("enabling firstlines") + end + setprop(par,a_firstline,specification) +end + +function firstlines.set(specification) + nuts.setparproperty(set,specification) +end + +implement { + name = "setfirstline", + actions = firstlines.set, + arguments = { + { + { "alternative" }, + { "font", "integer" }, + { "dynamic", "integer" }, + { "ma", "integer" }, + { "ca", "integer" }, + { "ta", "integer" }, + { "n", "integer" }, + } + } +} + +actions[v_line] = function(head,setting) + local dynamic = setting.dynamic + local font = setting.font + local noflines = setting.n or 1 + local ma = setting.ma or 0 + local ca = setting.ca + local ta = setting.ta + local hangafter = texget("hangafter") + local hangindent = texget("hangindent") + local parindent = texget("parindent") + local nofchars = 0 + local n = 0 + local temp = copy_node_list(head) + local linebreaks = { } + + local set = function(head) + for g in nextglyph, head do + if dynamic > 0 then + setglyphdata(g,dynamic) + end + setfont(g,font) + end + end + + set(temp) + + for g in nextdisc, temp do + local pre, post, replace = getdisc(g) + if pre then + set(pre) + end + if post then + set(post) + end + if replace then + set(replace) + end + end + + local start = temp + local list = temp + local prev = temp + for i=1,noflines do + local hsize = texget("hsize") - texget("leftskip",false) - texget("rightskip",false) + if i == 1 then + hsize = hsize - parindent + end + if i <= - hangafter then + hsize = hsize - hangindent + end + + local function list_dimensions(list,start) + local temp = copy_node_list(list,start) + temp = nodes.handlers.characters(temp) + temp = nodes.injections.handler(temp) + -- temp = typesetters.fontkerns.handler(temp) -- maybe when enabled + -- nodes.handlers.protectglyphs(temp) -- not needed as we discard + -- temp = typesetters.spacings.handler(temp) -- maybe when enabled + -- temp = typesetters.kerns.handler(temp) -- maybe when enabled + -- temp = typesetters.cases.handler(temp) -- maybe when enabled + local width = getdimensions(temp) + flush_node_list(temp) + return width + end + + local function try(extra) + local width = list_dimensions(list,start) + if extra then + width = width + list_dimensions(extra) + end + -- report_firstlines("line length: %p, progression: %p, text: %s",hsize,width,nodes.listtoutf(list,nil,nil,start)) + if width > hsize then + list = prev + return true + else + linebreaks[i] = n + prev = start + nofchars = n + end + end + + while start do + local id = getid(start) + if id == glyph_code then + -- go on + elseif id == disc_code then + -- this could be an option + n = n + 1 + local pre, post, replace = getdisc(start) + if pre and try(pre) then + break + elseif replace and try(replace) then + break + end + elseif id == kern_code then -- todo: fontkern + -- this could be an option + elseif id == glue_code then + n = n + 1 + if try() then + break + end + end + start = getnext(start) + end + if not linebreaks[i] then + linebreaks[i] = n + end + end + + flush_node_list(temp) + + local start = head + local n = 0 + + local function update(start) + if dynamic > 0 then + setglyphdata(start,dynamic) + end + setfont(start,font) + setcoloring(ma,ca,ta) + end + + for i=1,noflines do + local linebreak = linebreaks[i] + while start and n < nofchars do + local id = getid(start) + local ok = false + if id == glyph_code then + update(start) + elseif id == disc_code then + n = n + 1 + local disc = start + local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) + if linebreak == n then + local p, n = getboth(start) + if pre then + for current in nextglyph, pre do + update(current) + end + setlink(pretail,n) + setlink(p,pre) + start = pretail + pre = nil + else + setlink(p,n) + start = p + end + if post then + local p, n = getboth(start) + setlink(posttail,n) + setlink(start,post) + post = nil + end + else + local p, n = getboth(start) + if replace then + for current in nextglyph, replace do + update(current) + end + setlink(replacetail,n) + setlink(p,replace) + start = replacetail + replace = nil + else + setlink(p,n) + start = p + end + end + setdisc(disc,pre,post,replace) + flush_node(disc) + elseif id == glue_code then + n = n + 1 + if linebreak ~= n then + head = insert_node_before(head,start,newpenalty(10000)) -- nobreak + end + end + local next = getnext(start) + if linebreak == n then + if start ~= head then + local where = id == glue_code and getprev(start) or start + if trace_firstlines then + head, where = insert_node_after(head,where,newpenalty(10000)) -- nobreak + head, where = insert_node_after(head,where,newkern(-65536)) + head, where = insert_node_after(head,where,tracerrule(65536,4*65536,2*65536,"darkblue")) + end + head, where = insert_node_after(head,where,newpenalty(-10000)) -- break + end + start = next + break + end + start = next + end + end + + return head +end + +actions[v_word] = function(head,setting) + -- local attribute = fonts.specifiers.contextnumber(setting.feature) -- was experimental + local dynamic = setting.dynamic + local font = setting.font + local words = 0 + local nofwords = setting.n or 1 + local start = head + local ok = false + local ma = setting.ma or 0 + local ca = setting.ca + local ta = setting.ta + while start do + local id = getid(start) + -- todo: delete disc nodes + if id == glyph_code then + if not ok then + words = words + 1 + ok = true + end + setcoloring(ma,ca,ta) + if dynamic > 0 then + setglyphdata(start,dynamic) + end + setfont(start,font) + elseif id == disc_code then + -- continue + elseif id == kern_code then -- todo: fontkern + -- continue + else + ok = false + if words == nofwords then + break + end + end + start = getnext(start) + end + return head +end + +actions[v_default] = actions[v_line] + +function firstlines.handler(head) + if getid(head) == par_code and start_of_par(head) then + local settings = getprop(head,a_firstline) + if settings then + disableaction("processors","typesetters.firstlines.handler") + local alternative = settings.alternative or v_default + local action = actions[alternative] or actions[v_default] + if action then + if trace_firstlines then + report_firstlines("processing firstlines, alternative %a",alternative) + end + return action(head,settings) + end + end + end + return head +end + +-- goodie + +local function applytofirstcharacter(box,what) + local tbox = getbox(box) -- assumes hlist + local list = getlist(tbox) + local done = nil + for n in nextglyph, list do + list = remove_node(list,n) + done = n + break + end + if done then + setlist(tbox,list) + local kind = type(what) + if kind == "string" then + context[what](tonode(done)) + elseif kind == "function" then + what(done) + else + -- error + end + end +end + +implement { + name = "applytofirstcharacter", + actions = applytofirstcharacter, + arguments = { "integer", "string" } +} |