From aff2893a6d6652223e92e3de18e1260cbd533fea Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Sun, 6 Dec 2020 18:45:33 +0100 Subject: 2020-12-06 18:14:00 --- .../documents/general/manuals/luametatex.pdf | Bin 1307633 -> 1283945 bytes .../general/manuals/luametatex/luametatex.tex | 5 +- tex/context/base/mkii/cont-new.mkii | 2 +- tex/context/base/mkii/context.mkii | 2 +- tex/context/base/mkiv/cont-new.mkiv | 2 +- tex/context/base/mkiv/context.mkiv | 2 +- tex/context/base/mkiv/grph-rul.lua | 6 +- tex/context/base/mkiv/node-bck.lua | 3 - tex/context/base/mkiv/node-syn.lua | 5 +- tex/context/base/mkiv/status-files.pdf | Bin 26096 -> 26086 bytes tex/context/base/mkiv/status-lua.pdf | Bin 255677 -> 255761 bytes tex/context/base/mkiv/typo-spa.lua | 246 +++++++ tex/context/base/mkxl/back-pdf.mkxl | 1 + tex/context/base/mkxl/colo-nod.lmt | 40 ++ tex/context/base/mkxl/colo-nod.mkxl | 20 + tex/context/base/mkxl/cont-new.mkxl | 2 +- tex/context/base/mkxl/context.mkxl | 3 +- tex/context/base/mkxl/grph-rul.lmt | 124 ++++ tex/context/base/mkxl/grph-rul.mkxl | 2 +- tex/context/base/mkxl/lpdf-lmt.lmt | 57 +- tex/context/base/mkxl/lpdf-rul.lmt | 392 +++++++++++ tex/context/base/mkxl/node-bck.lmt | 294 +++++++++ tex/context/base/mkxl/node-bck.mkxl | 2 +- tex/context/base/mkxl/node-ini.mkxl | 2 +- tex/context/base/mkxl/node-ref.lmt | 10 +- tex/context/base/mkxl/node-rul.lmt | 203 ++---- tex/context/base/mkxl/node-rul.mkxl | 5 +- tex/context/base/mkxl/node-tra.lmt | 734 +++++++++++++++++++++ tex/context/base/mkxl/typo-drp.lmt | 346 ++++++++++ tex/context/base/mkxl/typo-drp.mkxl | 2 +- tex/context/base/mkxl/typo-fln.lmt | 406 ++++++++++++ tex/context/base/mkxl/typo-fln.mkxl | 2 +- tex/generic/context/luatex/luatex-fonts-merged.lua | 2 +- 33 files changed, 2727 insertions(+), 195 deletions(-) create mode 100644 tex/context/base/mkiv/typo-spa.lua create mode 100644 tex/context/base/mkxl/colo-nod.lmt create mode 100644 tex/context/base/mkxl/colo-nod.mkxl create mode 100644 tex/context/base/mkxl/grph-rul.lmt create mode 100644 tex/context/base/mkxl/lpdf-rul.lmt create mode 100644 tex/context/base/mkxl/node-bck.lmt create mode 100644 tex/context/base/mkxl/node-tra.lmt create mode 100644 tex/context/base/mkxl/typo-drp.lmt create mode 100644 tex/context/base/mkxl/typo-fln.lmt diff --git a/doc/context/documents/general/manuals/luametatex.pdf b/doc/context/documents/general/manuals/luametatex.pdf index 946f6db26..0b73a9556 100644 Binary files a/doc/context/documents/general/manuals/luametatex.pdf and b/doc/context/documents/general/manuals/luametatex.pdf differ diff --git a/doc/context/sources/general/manuals/luametatex/luametatex.tex b/doc/context/sources/general/manuals/luametatex/luametatex.tex index c75e8ec86..4251b8770 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex.tex @@ -38,8 +38,8 @@ % 20200610 : 258 pages % mingw all in: 10.6 -% 20201204 : 266 pages -% mingw all in: 10.3 +% 20201204 : 260 pages +% mingw all in: 10.0 % 20200720 : 258 pages / all in % @@ -107,7 +107,6 @@ \startbackmatter % \component luametatex-rejected % local file \component luametatex-registers - \component luametatex-differences \component luametatex-statistics \stopbackmatter diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 5039c26aa..d6a41419c 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2020.12.04 20:19} +\newcontextversion{2020.12.06 18:12} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index 8b103d931..d3ff08369 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2020.12.04 20:19} +\edef\contextversion{2020.12.06 18:12} %D For those who want to use this: diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index e77e90e59..b8b97f52b 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2020.12.04 20:19} +\newcontextversion{2020.12.06 18:12} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index fd88f9914..dc6bc1a4c 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -45,7 +45,7 @@ %D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2020.12.04 20:19} +\edef\contextversion{2020.12.06 18:12} %D Kind of special: diff --git a/tex/context/base/mkiv/grph-rul.lua b/tex/context/base/mkiv/grph-rul.lua index 5067b0165..0582a0ad0 100644 --- a/tex/context/base/mkiv/grph-rul.lua +++ b/tex/context/base/mkiv/grph-rul.lua @@ -87,6 +87,8 @@ do local replacer = utilities.templates.replacer + -- todo: RuleColor -> just string ? + local predefined = { ["fake:word"] = replacer [[ FakeWord(%width%,%height%,%depth%,%line%,%color%); @@ -133,7 +135,9 @@ def RuleColor = %color% enddef ; initialized = true simplemetapost("rulefun",formatters["randomseed := %s;"](getrandom("rulefun",0,4095))) end - local pdf = caching and cache[code] or simplemetapost("rulefun",code) -- w, h, d + -- we enable extensions but we could also consider to delegate colors + -- to the node finalizer + local pdf = caching and cache[code] or simplemetapost("rulefun",code,true) if trace_mp then report_mp("code: %s",code) report_mp("pdf : %s",pdf) diff --git a/tex/context/base/mkiv/node-bck.lua b/tex/context/base/mkiv/node-bck.lua index 82c800de8..ec84a3db5 100644 --- a/tex/context/base/mkiv/node-bck.lua +++ b/tex/context/base/mkiv/node-bck.lua @@ -63,9 +63,6 @@ local unsetvalue = attributes.unsetvalue local linefillers = nodes.linefillers -local a_color = privateattributes("color") -local a_transparency = privateattributes("transparency") -local a_colormodel = privateattributes("colormodel") local a_background = privateattributes("background") local a_alignbackground = privateattributes("alignbackground") local a_linefiller = privateattributes("linefiller") diff --git a/tex/context/base/mkiv/node-syn.lua b/tex/context/base/mkiv/node-syn.lua index b3ce4a7e3..d9c653abb 100644 --- a/tex/context/base/mkiv/node-syn.lua +++ b/tex/context/base/mkiv/node-syn.lua @@ -849,11 +849,12 @@ implement { actions = synctex.resume, } -interfaces.implement { +implement { name = "synctexpushline", actions = synctex.pushline, } -interfaces.implement { + +implement { name = "synctexpopline", actions = synctex.popline, } diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf index 22286e42d..f61ff2582 100644 Binary files a/tex/context/base/mkiv/status-files.pdf and b/tex/context/base/mkiv/status-files.pdf differ diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf index 844a0c16e..75f0d1787 100644 Binary files a/tex/context/base/mkiv/status-lua.pdf and b/tex/context/base/mkiv/status-lua.pdf differ diff --git a/tex/context/base/mkiv/typo-spa.lua b/tex/context/base/mkiv/typo-spa.lua new file mode 100644 index 000000000..78fc22964 --- /dev/null +++ b/tex/context/base/mkiv/typo-spa.lua @@ -0,0 +1,246 @@ +if not modules then modules = { } end modules ['typo-spa'] = { + version = 1.001, + comment = "companion to typo-spa.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, type = next, type + +local trace_spacing = false trackers.register("typesetters.spacing", function(v) trace_spacing = v end) + +local report_spacing = logs.reporter("typesetting","spacing") + +local nodes, fonts, node = nodes, fonts, node + +local fonthashes = fonts.hashes +local quaddata = fonthashes.quads + +local texsetattribute = tex.setattribute +local unsetvalue = attributes.unsetvalue + +local v_reset = interfaces.variables.reset + +local nuts = nodes.nuts + +local getnext = nuts.getnext +local getprev = nuts.getprev +local takeattr = nuts.takeattr +local isglyph = nuts.isglyph + +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 nodepool = nuts.pool +local new_penalty = nodepool.penalty +local new_glue = nodepool.glue + +local nodecodes = nodes.nodecodes +local math_code = nodecodes.math + +local somespace = nodes.somespace +local somepenalty = nodes.somepenalty + +local enableaction = nodes.tasks.enableaction + +typesetters = typesetters or { } +local typesetters = typesetters + +typesetters.spacings = typesetters.spacings or { } +local spacings = typesetters.spacings + +spacings.mapping = spacings.mapping or { } +spacings.numbers = spacings.numbers or { } + +local a_spacings = attributes.private("spacing") + +storage.register("typesetters/spacings/mapping", spacings.mapping, "typesetters.spacings.mapping") + +local mapping = spacings.mapping +local numbers = spacings.numbers + +for i=1,#mapping do + local m = mapping[i] + numbers[m.name] = m +end + +-- todo cache lastattr + +function spacings.handler(head) + local start = head + -- head is always begin of par (whatsit), so we have at least two prev nodes + -- penalty followed by glue + while start do + local char, id = isglyph(start) + if char then + local attr = takeattr(start,a_spacings) + if attr and attr > 0 then + local data = mapping[attr] + if data then + local map = data.characters[char] + if map then + local font = id + local left = map.left + local right = map.right + local alternative = map.alternative + local quad = quaddata[font] + local prev = getprev(start) + if left and left ~= 0 and prev then + local ok = false + local prevprev = getprev(prev) + if alternative == 1 then + local somespace = somespace(prev,true) + if somespace then + local somepenalty = somepenalty(prevprev,10000) + if somepenalty then + if trace_spacing then + report_spacing("removing penalty and space before %C (left)",char) + end + head = remove_node(head,prev,true) + head = remove_node(head,prevprev,true) + else + if trace_spacing then + report_spacing("removing space before %C (left)",char) + end + head = remove_node(head,prev,true) + end + end + ok = true + else + ok = not (somespace(prev,true) and somepenalty(prevprev,true)) or somespace(prev,true) + end + if ok then + if trace_spacing then + report_spacing("inserting penalty and space before %C (left)",char) + end + insert_node_before(head,start,new_penalty(10000)) + insert_node_before(head,start,new_glue(left*quad)) + end + end + local next = getnext(start) + if right and right ~= 0 and next then + local ok = false + local nextnext = getnext(next) + if alternative == 1 then + local somepenalty = somepenalty(next,10000) + if somepenalty then + local somespace = somespace(nextnext,true) + if somespace then + if trace_spacing then + report_spacing("removing penalty and space after %C right",char) + end + head = remove_node(head,next,true) + head = remove_node(head,nextnext,true) + end + else + local somespace = somespace(next,true) + if somespace then + if trace_spacing then + report_spacing("removing space after %C (right)", char) + end + head = remove_node(head,next,true) + end + end + ok = true + else + ok = not (somepenalty(next,10000) and somespace(nextnext,true)) or somespace(next,true) + end + if ok then + if trace_spacing then + report_spacing("inserting penalty and space after %C (right)",char) + end + insert_node_after(head,start,new_glue(right*quad)) + insert_node_after(head,start,new_penalty(10000)) + end + end + end + end + end + elseif id == math_code then + start = end_of_math(start) -- weird, can return nil .. no math end? + end + if start then + start = getnext(start) + end + end + return head +end + +local enabled = false + +function spacings.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 spacings.setup(name,char,settings) + local data = numbers[name] + if not data then + -- error + else + data.characters[char] = settings + end +end + +function spacings.set(name) + local n = unsetvalue + if name ~= v_reset then + local data = numbers[name] + if data then + if not enabled then + enableaction("processors","typesetters.spacings.handler") + enabled = true + end + n = data.number or unsetvalue + end + end + texsetattribute(a_spacings,n) +end + +function spacings.reset() + texsetattribute(a_spacings,unsetvalue) +end + +-- interface + +local implement = interfaces.implement + +implement { + name = "definecharacterspacing", + actions = spacings.define, + arguments = "string" +} + +implement { + name = "setupcharacterspacing", + actions = spacings.setup, + arguments = { + "string", + "integer", + { + { "left", "number" }, + { "right", "number" }, + { "alternative", "integer" }, + } + } +} + +implement { + name = "setcharacterspacing", + actions = spacings.set, + arguments = "string" +} diff --git a/tex/context/base/mkxl/back-pdf.mkxl b/tex/context/base/mkxl/back-pdf.mkxl index 171c6e7b6..66db06ef1 100644 --- a/tex/context/base/mkxl/back-pdf.mkxl +++ b/tex/context/base/mkxl/back-pdf.mkxl @@ -39,6 +39,7 @@ \registerctxluafile{lpdf-epa}{autosuffix} \registerctxluafile{lpdf-emb}{autosuffix,optimize} \registerctxluafile{lpdf-fnt}{autosuffix} +\registerctxluafile{lpdf-rul}{autosuffix} \registerctxluafile{back-pdp}{autosuffix} \registerctxluafile{back-pdf}{autosuffix} % some code will move to lpdf-* diff --git a/tex/context/base/mkxl/colo-nod.lmt b/tex/context/base/mkxl/colo-nod.lmt new file mode 100644 index 000000000..d65f5978c --- /dev/null +++ b/tex/context/base/mkxl/colo-nod.lmt @@ -0,0 +1,40 @@ +if not modules then modules = { } end modules ['node-tra'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local nuts = nodes.nuts + +local setattrs = nuts.setattrs + +local privateattributes = attributes.private + +local a_color = privateattributes('color') +local a_transparency = privateattributes('transparency') +local a_colormodel = privateattributes('colormodel') + +local colors = { } +nuts.colors = colors + +function colors.set(n,ma,ca,ta) -- we could also do layers here + if ca then + if ca > 0 then + if not ma or ma == 0 then + ma = 1 + end + if ta then + setattrs(n,a_colorspace,ma,a_color,ca,a_transparency,ta) + else + setattrs(n,a_colorspace,ma,a_color,ca) + end + end + elseif ta then + if ta > 0 then + setattr(n,a_transparency,ta) + end + end + return n +end diff --git a/tex/context/base/mkxl/colo-nod.mkxl b/tex/context/base/mkxl/colo-nod.mkxl new file mode 100644 index 000000000..3cc3db033 --- /dev/null +++ b/tex/context/base/mkxl/colo-nod.mkxl @@ -0,0 +1,20 @@ +%D \module +%D [ file=colo-nod, +%D version=2020.12.03, +%D title=\CONTEXT\ Color Macros, +%D subtitle=Nodes, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Color Macros / Nodes} + +%D In \LMTX\ we moved some code here. + +\registerctxluafile{colo-nod}{} + +\endinput diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index f56aaae6a..3c11528a8 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2020.12.04 20:19} +\newcontextversion{2020.12.06 18:12} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 40fca9fd9..5eca52575 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@ %D {YYYY.MM.DD HH:MM} format. \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2020.12.04 20:19} +\immutable\edef\contextversion{2020.12.06 18:12} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error @@ -220,6 +220,7 @@ \loadmkxlfile{core-dat} \loadmkxlfile{colo-ini} +\loadmkxlfile{colo-nod} \loadmkxlfile{colo-grp} % optional \loadmkxlfile{colo-ext} diff --git a/tex/context/base/mkxl/grph-rul.lmt b/tex/context/base/mkxl/grph-rul.lmt new file mode 100644 index 000000000..d8da5f760 --- /dev/null +++ b/tex/context/base/mkxl/grph-rul.lmt @@ -0,0 +1,124 @@ +if not modules then modules = { } end modules ['grph-rul'] = { + version = 1.001, + comment = "companion to grph-rul.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local attributes = attributes +local nodes = nodes +local context = context + +local nuts = nodes.nuts +local nodepool = nuts.pool +local userrule = nuts.rules.userrule +local outlinerule = nuts.pool.outlinerule +local ruleactions = nuts.rules.ruleactions + +local setattrlist = nuts.setattrlist +local setattr = nuts.setattr +local tonode = nuts.tonode + +local getattribute = tex.getattribute + +local a_color = attributes.private('color') +local a_transparency = attributes.private('transparency') +local a_colormodel = attributes.private('colormodel') + +local floor = math.floor +local getrandom = utilities.randomizer.get + +do + + local function unsupported() end + + ruleactions.mp = unsupported + ruleactions.fill = unsupported + ruleactions.draw = unsupported + ruleactions.stroke = unsupported + ruleactions.box = unsupported + +end + +interfaces.implement { + name = "frule", + arguments = { { + { "width", "dimension" }, + { "height", "dimension" }, + { "depth", "dimension" }, + { "radius", "dimension" }, + { "line", "dimension" }, + { "type", "string" }, + { "data", "string" }, + { "name", "string" }, + { "radius", "dimension" }, + { "corner", "string" }, + } } , + actions = function(t) + local rule = userrule(t) + if t.type == "mp" then + t.ma = getattribute(a_colormodel) or 1 + t.ca = getattribute(a_color) + t.ta = getattribute(a_transparency) + else + setattrlist(rule,true) + end + context(tonode(rule)) -- will become context.nodes.flush + end +} + +interfaces.implement { + name = "outlinerule", + public = true, + protected = true, + arguments = { { + { "width", "dimension" }, + { "height", "dimension" }, + { "depth", "dimension" }, + { "line", "dimension" }, + } } , + actions = function(t) + local rule = outlinerule(t.width,t.height,t.depth,t.line) + setattrlist(rule,true) + context(tonode(rule)) -- will become context.nodes.flush + end +} + +interfaces.implement { + name = "framedoutline", + arguments = { "dimension", "dimension", "dimension", "dimension" }, + actions = function(w,h,d,l) + local rule = outlinerule(w,h,d,l) + setattrlist(rule,true) + context(tonode(rule)) -- will become context.nodes.flush + end +} + +interfaces.implement { + name = "fakeword", + arguments = { { + { "factor", "dimension" }, + { "name", "string" }, -- can be type + { "min", "dimension" }, + { "max", "dimension" }, + { "n", "integer" }, + } } , + actions = function(t) + local factor = t.factor or 0 + local amount = getrandom("fakeword",t.min,t.max) + local rule = userrule { + height = 1.25*factor, + depth = 0.25*factor, + width = floor(amount/10000) * 10000, + line = 0.10*factor, + ma = getattribute(a_colormodel) or 1, + ca = getattribute(a_color), + ta = getattribute(a_transparency), + type = "mp", + name = t.name, + } + setattrlist(rule,true) + context(tonode(rule)) + end +} diff --git a/tex/context/base/mkxl/grph-rul.mkxl b/tex/context/base/mkxl/grph-rul.mkxl index ef65b1fe4..034e91734 100644 --- a/tex/context/base/mkxl/grph-rul.mkxl +++ b/tex/context/base/mkxl/grph-rul.mkxl @@ -13,7 +13,7 @@ \writestatus{loading}{ConTeXt Graphic Macros / Rule Trickery} -\registerctxluafile{grph-rul}{} +\registerctxluafile{grph-rul}{autosuffix} \unprotect diff --git a/tex/context/base/mkxl/lpdf-lmt.lmt b/tex/context/base/mkxl/lpdf-lmt.lmt index cbad20f09..d1c5e59f7 100644 --- a/tex/context/base/mkxl/lpdf-lmt.lmt +++ b/tex/context/base/mkxl/lpdf-lmt.lmt @@ -815,6 +815,7 @@ local flushsave, flushrestore, flushsetmatrix do local m = p.matrix if m then local rx, sx, sy, ry = unpack(m) + local s if not rx then rx = 1 elseif rx == 0 then @@ -832,6 +833,16 @@ local flushsave, flushrestore, flushsetmatrix do sy = 0 end -- + if sx == 0 and sy == 0 then + if rx == 1 and ry == 1 then + s = s_matrix_0 + else + s = f_matrix_2(rx,ry) + end + else + s = f_matrix_4(rx,sx,sy,ry) + end + -- if shippingmode == "page" then local tx = pos_h * (1 - rx) - pos_v * sy local ty = pos_v * (1 - ry) - pos_h * sx @@ -850,15 +861,7 @@ local flushsave, flushrestore, flushsetmatrix do pdf_set_pos(pos_h,pos_v) -- b = b + 1 - if sx == 0 and sy == 0 then - if rx == 1 and ry == 1 then - buffer[b] = s_matrix_0 - else - buffer[b] = f_matrix_2(rx,ry) - end - else - buffer[b] = f_matrix_4(rx,sx,sy,ry) - end + buffer[b] = s end end end @@ -903,15 +906,15 @@ local localconverter = nil -- will be set local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do - local rulecodes = nodes.rulecodes - local newrule = nodes.pool.rule + local rulecodes = nodes.rulecodes + local newrule = nodes.pool.rule - local setprop = nuts.setprop - local getprop = nuts.getprop + local setprop = nuts.setprop + local getprop = nuts.getprop - local getwhd = nuts.getwhd - local flushlist = nuts.flush_list - local getdata = nuts.getdata + local getwhd = nuts.getwhd + local flushlist = nuts.flush_list + local getdata = nuts.getdata local normalrule_code = rulecodes.normal local boxrule_code = rulecodes.box @@ -924,24 +927,24 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do local radicalrule_code = rulecodes.radical local outlinerule_code = rulecodes.outline - local rule_callback = callbacks.functions.process_rule + local processrule = nodes.rules.process local f_fm = formatters["/Fm%d Do"] local f_im = formatters["/Im%d Do"] local f_gr = formatters["/Gp%d Do"] - local s_b = "q" - local s_e = "Q" + local s_b = "q" + local s_e = "Q" - local f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"] - local f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"] + local f_v = formatters["[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S"] + local f_h = formatters["[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S"] - local f_f = formatters["0 0 %.6N %.6N re f"] - local f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"] - local f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"] + local f_f = formatters["0 0 %.6N %.6N re f"] + local f_o = formatters["[] 0 d 0 J 0 0 %.6N %.6N re S"] + local f_w = formatters["[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S"] - local f_b = formatters["%.6N w 0 %.6N %.6N %.6N re f"] - local f_x = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S"] + local f_b = formatters["%.6N w 0 %.6N %.6N %.6N re f"] + local f_x = formatters["[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S"] -- Historically the index is an object which is kind of bad. @@ -1291,7 +1294,7 @@ local flushrule, flushsimplerule, flushspecialrule, flushimage, flushgroup do pdf_goto_pagemode() b = b + 1 ; buffer[b] = s_b pdf_set_pos_temp(pos_h,pos_v) - rule_callback(current,size_h,size_v,pos_r) -- so we pass direction + processrule(current,size_h,size_v,pos_r) -- so we pass direction b = b + 1 ; buffer[b] = s_e return end diff --git a/tex/context/base/mkxl/lpdf-rul.lmt b/tex/context/base/mkxl/lpdf-rul.lmt new file mode 100644 index 000000000..b3df0f0b7 --- /dev/null +++ b/tex/context/base/mkxl/lpdf-rul.lmt @@ -0,0 +1,392 @@ +if not modules then modules = { } end modules ['lpdf-rul'] = { + version = 1.001, + comment = "companion to grph-rul.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local tonumber, next, type = tonumber, next, type +local concat = table.concat + +local attributes = attributes +local nodes = nodes + +local bpfactor = number.dimenfactors.bp + +local nuts = nodes.nuts +local ruleactions = nuts.rules.ruleactions + +local getwhd = nuts.getwhd + +local lefttoright_code = nodes.dirvalues.lefttoright + +local mpcolor = attributes.colors.mpcolor + +local trace_mp = false trackers.register("rules.mp", function(v) trace_mp = v end) + +local report_mp = logs.reporter("rules","mp") + +local floor = math.floor +local getrandom = utilities.randomizer.get +local formatters = string.formatters + +-- This is very pdf specific. Maybe move some to lpdf-rul.lua some day. + +local pdfprint + +pdfprint = function(...) pdfprint = lpdf.print return pdfprint(...) end + +-- updaters.register("backend.update",function() +-- pdfprint = lpdf.print +-- end) + +do + + local simplemetapost = metapost.simple + local cachesize = 0 + local maxcachesize = 256*1024 + local cachethreshold = 1024 + local caching = false -- otherwise random issues so we need a dedicated randomizer first + + -- local maxcachesize = 8*1024 + -- local cachethreshold = 1024/2 + + local cache = table.setmetatableindex(function(t,k) + local v = simplemetapost("rulefun",k) -- w, h, d + cachesize = cachesize + #v + if cachesize > maxcachesize then + -- print("old",cachesize) + for k, v in next, t do + local n = #v + if n > cachethreshold then + t[k] = nil + cachesize = cachesize - n + end + end + -- print("new",cachesize) + end + -- print(cachesize,maxcachesize,cachethreshold,#v) + t[k] = v + return v + end) + + local replacer = utilities.templates.replacer + + -- todo: RuleColor -> just string ? + + local predefined = { + ["fake:word"] = replacer [[ +FakeWord(%width%,%height%,%depth%,%line%,%color%); + ]], + ["fake:rule"] = replacer[[ +%initializations% +FakeRule(%width%,%height%,%depth%,%line%,%color%); + ]], + ["fake:rest"] = replacer [[ +RuleDirection := "%direction%" ; +RuleOption := "%option%" ; +RuleWidth := %width% ; +RuleHeight := %height% ; +RuleDepth := %depth% ; +RuleH := %h% ; +RuleV := %v% ; +RuleThickness := %line% ; +RuleFactor := %factor% ; +RuleOffset := %offset% ; +def RuleColor = %color% enddef ; +%data%; + ]] + } + + local initialized = false ; + + rule_mp = function(p,h,v,i,n) + local name = p.name or "fake:rest" + local code = (predefined[name] or predefined["fake:rest"]) { + data = p.data or "", + width = p.width * bpfactor, + height = p.height * bpfactor, + depth = p.depth * bpfactor, + factor = (p.factor or 0) * bpfactor, -- needs checking + offset = p.offset or 0, + line = (p.line or 65536) * bpfactor, + color = mpcolor(p.ma,p.ca,p.ta), + option = p.option or "", + direction = p.direction or lefttoright_code, + h = h * bpfactor, + v = v * bpfactor, + } + if not initialized then + initialized = true + simplemetapost("rulefun",formatters["randomseed := %s;"](getrandom("rulefun",0,4095))) + end + -- we enable extensions but we could also consider to delegate colors + -- to the node finalizer + local pdf = caching and cache[code] or simplemetapost("rulefun",code,true) + if trace_mp then + report_mp("code: %s",code) + report_mp("pdf : %s",pdf) + end + if pdf and pdf ~= "" then + pdfprint("direct",pdf) + end + end + + updaters.register("backend.update.lpdf",function() + ruleactions.mp = rule_mp + end) + +end + +do + + -- This is the old oval method that we keep it for compatibility reasons. Of + -- course one can use mp instead. It could be improved but at the cost of more + -- code than I'm willing to add for something hardly used. + + local function round(p,kind) + local method = tonumber(p.corner) or 0 + if method < 0 or method > 27 then + method = 0 + end + local width = p.width or 0 + local height = p.height or 0 + local depth = p.depth or 0 + local total = height + depth + local radius = p.radius or 655360 + local line = p.line or 65536 + local how = (method > 8 or kind ~= "fill") and "S" or "f" + local half = line / 2 + local xmin = half * bpfactor + local xmax = ( width - half) * bpfactor + local ymax = ( height - half) * bpfactor + local ymin = (-depth + half) * bpfactor + local full = ( radius + half) + local xxmin = full * bpfactor + local xxmax = ( width - full) * bpfactor + local yymax = ( height - full) * bpfactor + local yymin = (-depth + full) * bpfactor + line = line * bpfactor + if xxmin <= xxmax and yymin <= yymax then + local list = nil + if method == 0 then + list = { + "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax, + xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q", + } + elseif method == 1 then + list = { + "q", line, "w", xxmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, ymax, "l", xmin, ymax, "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", + "h", how, "Q", + } + elseif method == 2 then + list = { + "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax, + "l", xmin, ymax, xmin, yymax, "y", xmin, yymin, "l", xmin, ymin, xxmin, ymin, + "y", "h", how, "Q", + } + elseif method == 3 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax, + xxmax, ymax, "y", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, + "l", "h", how, "Q", + } + + elseif method == 4 then + list = { + "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l", + "h", how, "Q", + } + elseif method == 5 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, yymax, "l", xmax, ymax, + xxmax, ymax, "y", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 6 then + list = { + "q", line, "w", xmin, ymin, "m", xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", + xmax, ymax, "l", xmin, ymax, "l", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 7 then + list = { + "q", line, "w", xxmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xmin, ymax, + "l", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", "h", how, "Q", + } + elseif method == 8 then + list = { + "q", line, "w", xmin, ymin, "m", xmax, ymin, "l", xmax, ymax, "l", xxmin, ymax, + "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", "h", how, "Q", + } + elseif method == 9 then + list = { + "q", line, "w", xmin, ymax, "m", xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", + xxmax, ymin, "l", xmax, ymin, xmax, yymin, "y", xmax, ymax, "l", how, "Q", + } + elseif method == 10 then + list = { + "q", line, "w", xmax, ymax, "m", xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", + xmin, yymin, "l", xmin, ymin, xxmin, ymin, "y", xmax, ymin, "l", how, "Q", + } + elseif method == 11 then + list = { + "q", line, "w", xmax, ymin, "m", xmax, yymax, "l", xmax, ymax, xxmax, ymax, "y", + xxmin, ymax, "l", xmin, ymax, xmin, yymax, "y", xmin, ymin, "l", how, "Q", + } + elseif method == 12 then + list = { + "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y", + xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y", xmin, ymin, "l", how, "Q", + } + elseif method == 13 then + list = { + "q", line, "w", xmin, ymax, "m", xxmax, ymax, "l", xmax, ymax, xmax, yymax, "y", + xmax, ymin, "l", how, "Q", + } + elseif method == 14 then + list = { + "q", line, "w", xmax, ymax, "m", xmax, yymin, "l", xmax, ymin, xxmax, ymin, "y", + xmin, ymin, "l", how, "Q", + } + elseif method == 15 then + list = { + "q", line, "w", xmax, ymin, "m", xxmin, ymin, "l", xmin, ymin, xmin, yymin, "y", + xmin, ymax, "l", how, "Q", + } + elseif method == 16 then + list = { + "q", line, "w", xmin, ymin, "m", xmin, yymax, "l", xmin, ymax, xxmin, ymax, "y", + xmax, ymax, "l", how, "Q", + } + elseif method == 17 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", how, "Q", + } + elseif method == 18 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", how, "Q", + } + elseif method == 19 then + list = { + "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", how, "Q", + } + elseif method == 20 then + list = { + "q", line, "w", xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 21 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 22 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m", + xmax, ymin, xxmax, ymin, "y", how, "Q", + } + elseif method == 23 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m", + xmin, ymin, xmin, yymin, "y", how, "Q", + } + elseif method == 24 then + list = { + "q", line, "w", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 25 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xmax, yymin, "m", + xmax, ymin, xxmax, ymin, "y", xxmin, ymin, "m", xmin, ymin, xmin, yymin, "y", + xmin, yymax, "m", xmin, ymax, xxmin, ymax, "y", how, "Q", + } + elseif method == 26 then + list = { + "q", line, "w", xmax, yymin, "m", xmax, ymin, xxmax, ymin, "y", xmin, yymax, "m", + xmin, ymax, xxmin, ymax, "y", how, "Q", + } + + elseif method == 27 then + list = { + "q", line, "w", xxmax, ymax, "m", xmax, ymax, xmax, yymax, "y", xxmin, ymin, "m", + xmin, ymin, xmin, yymin, "y", how, "Q", + } + end + pdfprint("direct",concat(list," ")) + end + end + + local f_rectangle = formatters["%.6N w %.6N %.6N %.6N %.6N re %s"] + local f_baselined = formatters["%.6N w %.6N %.6N %.6N %.6N re s %.6N %.6N m %.6N %.6N l s"] + local f_dashlined = formatters["%.6N w %.6N %.6N %.6N %.6N re s [%.6N %.6N] 2 d %.6N %.6N m %.6N %.6N l s"] + local f_radtangle = formatters[ +[[%.6N w %.6N %.6N m +%.6N %.6N l %.6N %.6N %.6N %.6N y +%.6N %.6N l %.6N %.6N %.6N %.6N y +%.6N %.6N l %.6N %.6N %.6N %.6N y +%.6N %.6N l %.6N %.6N %.6N %.6N y +h %s]] + ] + + local rule_any = function(p,h,v,i,n) + if p.corner then + return round(p,i) + else + local l = (p.line or 65536)*bpfactor + local r = p and (p.radius or 0)*bpfactor or 0 + local w = h * bpfactor + local h = v * bpfactor + local m = nil + local t = i == "fill" and "f" or "s" + local o = l / 2 + if r > 0 then + w = w - o + h = h - o + m = f_radtangle(l, r,o, w-r,o, w,o,w,r, w,h-r, w,h,w-r,h, r,h, o,h,o,h-r, o,r, o,o,r,o, t) + else + w = w - l + h = h - l + m = f_rectangle(l,o,o,w,h,t) + end + pdfprint("direct",m) + end + end + + local function rule_box(p,h,v,i,n) + local w, h, d = getwhd(n) + local line = p.line or 65536 + local l = line *bpfactor + local w = w * bpfactor + local h = h * bpfactor + local d = d * bpfactor + local o = l / 2 + if (d >= 0 and h >= 0) or (d <= 0 and h <= 0) then + local dashed = tonumber(p.dashed) + if dashed and dashed > 5*line then + dashed = dashed * bpfactor + local delta = (w - 2*dashed*floor(w/(2*dashed)))/2 + pdfprint("direct",f_dashlined(l,o,o,w-l,h+d-l,dashed,dashed,delta,d,w-delta,d)) + else + pdfprint("direct",f_baselined(l,o,o,w-l,h+d-l,0,d,w,d)) + end + else + pdfprint("direct",f_rectangle(l,o,o,w-l,h+d-l)) + end + end + + updaters.register("backend.update.lpdf",function() + ruleactions.fill = rule_any + ruleactions.draw = rule_any + ruleactions.stroke = rule_any + ruleactions.box = rule_box + end) + +end + + + ruleactions.mp = unsupported + + diff --git a/tex/context/base/mkxl/node-bck.lmt b/tex/context/base/mkxl/node-bck.lmt new file mode 100644 index 000000000..9dff3ac40 --- /dev/null +++ b/tex/context/base/mkxl/node-bck.lmt @@ -0,0 +1,294 @@ +if not modules then modules = { } end modules ['node-bck'] = { + version = 1.001, + optimize = true, + comment = "companion to node-bck.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- beware, this one takes quite some runtime, so we need a status flag +-- maybe some page related state + +-- todo: done (or just get rid of done altogether) ... saves no purpose +-- any longer + +local attributes, nodes, node = attributes, nodes, node + +local enableaction = nodes.tasks.enableaction + +local nodecodes = nodes.nodecodes +local listcodes = nodes.listcodes + +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist + +local alignmentlist_code = listcodes.alignment +local celllist_code = listcodes.cell + +local nuts = nodes.nuts +local nodepool = nuts.pool + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getlist = nuts.getlist +local getattr = nuts.getattr +local getsubtype = nuts.getsubtype +local getwhd = nuts.getwhd +local getwidth = nuts.getwidth +local getprop = nuts.getprop + +local setattr = nuts.setattr +local setlink = nuts.setlink +local setlist = nuts.setlist +local setattributelist = nuts.setattributelist +local setprop = nuts.setprop + +local takebox = nuts.takebox +local findtail = nuts.tail + +local nextnode = nuts.traversers.node +local nexthlist = nuts.traversers.hlist +local nextlist = nuts.traversers.list + +local flush_node_list = nuts.flush_list + +local new_rule = nodepool.rule +local new_kern = nodepool.kern +local new_hlist = nodepool.hlist + +local privateattributes = attributes.private +local unsetvalue = attributes.unsetvalue + +local linefillers = nodes.linefillers + +local a_background = privateattributes("background") +local a_alignbackground = privateattributes("alignbackground") +local a_linefiller = privateattributes("linefiller") +local a_ruled = privateattributes("ruled") + +local trace_alignment = false +local report_alignment = logs.reporter("backgrounds","alignment") + +trackers.register("backgrounds.alignments",function(v) trace_alignment = v end) + +-- We can't use listbuilders with where=alignment because at that stage we have +-- unset boxes. Also, post_linebreak is unsuitable for nested processing as we +-- get the same stuff many times (wrapped again and again). +-- +-- After many experiments with different callbacks the shipout is still the best +-- place but then we need to store some settings longer or save them with the node. +-- For color only we can get away with it with an extra attribute flagging a row +-- but for more complex stuff we can better do as we do here now. + +local overshoot = math.floor(65781/5) -- could be an option per table (just also store it) + +local function colored_a(current,list,template,id) + local width, height, depth = getwhd(current) + local total = height + depth + if width > 0 and total > 0 then + local rule = nil + -- + local a = getattr(template,a_linefiller) + if a then + local d = linefillers.data[a%1000] + if d then + rule = linefillers.filler(template,d,width,height,depth) + end + end + -- + if not rule then + rule = new_rule(width,height,depth) + setattributelist(rule,template) + end + local back = new_kern(-((id == vlist_code and total) or width)) + return setlink(rule,back,list) + end +end + +local function colored_b(current,list,template,id,indent) + local width, height, depth = getwhd(current) + local total = height + depth + if width > 0 and total > 0 then + local fore = (indent ~= 0) and new_kern(indent) + local rule = nil + -- + local a = getattr(template,a_linefiller) + if a then + local d = linefillers.data[a%1000] + if d then + rule = linefillers.filler(template,d,width-indent,height,depth) + end + end + -- + if not rule then + rule = new_rule(width-indent,height+overshoot,depth+overshoot) + setattributelist(rule,template) + end + if overshoot == 0 then + local back = new_kern(-((id == vlist_code and total) or width)) + return setlink(fore,rule,back,list) + else + rule = new_hlist(rule) + return setlink(fore,rule,list) + end + end +end + +local templates = { } +local currentrow = 0 +local enabled = false +local alignments = false + +local function add_alignbackgrounds(head,list) + for current, id, subtype, list in nextlist, list do + if list and id == hlist_code and subtype == celllist_code then + for template in nexthlist, list do + local background = getattr(template,a_alignbackground) + if background then + local list = colored_a(current,list,template) + if list then + setlist(current,list) + end + setattr(template,a_alignbackground,unsetvalue) -- or property + end + break + end + end + end + local template = getprop(head,"alignmentchecked") + if template then + list = colored_b(head,list,template[1],hlist_code,template[2]) + flush_node_list(template) + templates[currentrow] = false + return list + end +end + +local function add_backgrounds(head,id,list) + if list then + for current, id, subtype, list in nextlist, list do + if list then + if alignments and subtype == alignmentlist_code then + local l = add_alignbackgrounds(current,list) + if l then + list = l + setlist(current,list) + end + end + local l = add_backgrounds(current,id,list) + if l then + list = l + setlist(current,l) + end + end + end + end + if id == hlist_code or id == vlist_code then + local background = getattr(head,a_background) + if background then + list = colored_a(head,list,head,id) + -- not needed + setattr(head,a_background,unsetvalue) -- or property -- todo + return list + end + end +end + +function nodes.handlers.backgrounds(head) + add_backgrounds(head,getid(head),getlist(head)) + return head +end + +function nodes.handlers.backgroundspage(head,where) + if head and where == "alignment" then + for n in nexthlist, head do + local p = getprop(n,"alignmentchecked") + if not p and getsubtype(n) == alignmentlist_code then + currentrow = currentrow + 1 + local template = templates[currentrow] + if trace_alignment then + report_alignment("%03i %s %s",currentrow,"page",template and "+" or "-") + end + setprop(n,"alignmentchecked",template) + end + end + end + return head +end + +function nodes.handlers.backgroundsvbox(head,where) + if head and where == "vbox" then + local list = getlist(head) + if list then + for n in nexthlist, list do + local p = getprop(n,"alignmentchecked") + if not p and getsubtype(n) == alignmentlist_code then + currentrow = currentrow + 1 + local template = templates[currentrow] + if trace_alignment then + report_alignment("%03i %s %s",currentrow,"vbox",template and "+" or "-") + end + setprop(n,"alignmentchecked",template) + end + end + end + end + return head +end + +-- interfaces.implement { +-- name = "enablebackgroundboxes", +-- onlyonce = true, +-- actions = enableaction, +-- arguments = { "'shipouts'", "'nodes.handlers.backgrounds'" } +-- } +-- +-- doing it in the shipout works as well but this is nicer + +local function enable(alignmentstoo) + if not enabled then + enabled = true + enableaction("shipouts","nodes.handlers.backgrounds") + end + if not alignments and alignmentstoo then + alignments = true + enableaction("vboxbuilders","nodes.handlers.backgroundsvbox") + enableaction("mvlbuilders", "nodes.handlers.backgroundspage") + end +end + +interfaces.implement { + name = "enablebackgroundboxes", + onlyonce = true, + actions = enable, +} + +interfaces.implement { + name = "enablebackgroundalign", + onlyonce = true, + actions = function() + enable(true) + end, +} + +interfaces.implement { + name = "setbackgroundrowdata", + arguments = { "integer", "integer", "dimension" }, + actions = function(row,box,indent) + row = row -1 -- better here than in tex + if box == 0 then + templates[row] = false + else + templates[row] = { takebox(box), indent } + end + end, +} + +interfaces.implement { + name = "resetbackgroundrowdata", + actions = function() + currentrow = 0 + end, +} diff --git a/tex/context/base/mkxl/node-bck.mkxl b/tex/context/base/mkxl/node-bck.mkxl index 0da516c88..b456313df 100644 --- a/tex/context/base/mkxl/node-bck.mkxl +++ b/tex/context/base/mkxl/node-bck.mkxl @@ -19,7 +19,7 @@ \unprotect -\registerctxluafile{node-bck}{optimize} +\registerctxluafile{node-bck}{autosuffix,optimize} % \backgroundvbox[green] {\input tufte } \par % \backgroundvbox[blue] {\input ward } \par diff --git a/tex/context/base/mkxl/node-ini.mkxl b/tex/context/base/mkxl/node-ini.mkxl index a7343469e..84f46e546 100644 --- a/tex/context/base/mkxl/node-ini.mkxl +++ b/tex/context/base/mkxl/node-ini.mkxl @@ -26,7 +26,7 @@ \registerctxluafile{node-aux}{autosuffix} \registerctxluafile{node-gcm}{autosuffix} \registerctxluafile{node-tst}{} -\registerctxluafile{node-tra}{} % we might split it off (module) +\registerctxluafile{node-tra}{autosuffix} \registerctxluafile{node-snp}{autosuffix} \registerctxluafile{node-tsk}{} \registerctxluafile{node-tex}{autosuffix} diff --git a/tex/context/base/mkxl/node-ref.lmt b/tex/context/base/mkxl/node-ref.lmt index 98c600aa0..06c9981a9 100644 --- a/tex/context/base/mkxl/node-ref.lmt +++ b/tex/context/base/mkxl/node-ref.lmt @@ -425,15 +425,14 @@ end local colorize, justadd do - local a_color = attributes.private('color') - local a_colormodel = attributes.private('colormodel') - local a_transparency = attributes.private('transparency') local u_transparency = nil local u_colors = { } local force_gray = true local register_color = colors.register + local setcoloring = nuts.colors.set + local function addstring(what,str,shift) --todo make a pluggable helper (in font-ctx) if str then local typesetters = nuts.typesetters @@ -486,10 +485,7 @@ local colorize, justadd do height = 65536/2 depth = height end - local rule = new_rule(width,height,depth) -- todo: use tracer rule - setattr(rule,a_colormodel,1) -- gray color model - setattr(rule,a_color,u_color) - setattr(rule,a_transparency,u_transparency) + local rule = setcoloring(new_rule(width,height,depth),1,u_color,u_transparency) -- gray color model if width < 0 then local kern = new_kern(width) setwidth(rule,-width) diff --git a/tex/context/base/mkxl/node-rul.lmt b/tex/context/base/mkxl/node-rul.lmt index 72a257872..99c5d98a4 100644 --- a/tex/context/base/mkxl/node-rul.lmt +++ b/tex/context/base/mkxl/node-rul.lmt @@ -40,6 +40,7 @@ local getid = nuts.getid local getdirection = nuts.getdirection local getattr = nuts.getattr local setattr = nuts.setattr +local setattrs = nuts.setattrs local getfont = nuts.getfont local getsubtype = nuts.getsubtype local getlist = nuts.getlist @@ -67,7 +68,8 @@ local getrangedimensions = nuts.rangedimensions local hpack_nodes = nuts.hpack local copy_list = nuts.copy_list -local nexthlist = nuts.traversers.hlist +local nextlist = nuts.traversers.list +local nextglue = nuts.traversers.glue local nodecodes = nodes.nodecodes local rulecodes = nodes.rulecodes @@ -83,9 +85,11 @@ local hlist_code = nodecodes.hlist local indentlist_code = listcodes.indent local linelist_code = listcodes.line -local leftskip_code = gluecodes.leftskip -local rightskip_code = gluecodes.rightskip -local parfillskip_code = gluecodes.parfillskip +local leftskip_code = gluecodes.leftskip +local rightskip_code = gluecodes.rightskip +local parfillleftskip_code = gluecodes.parfillleftskip +local parfillrightskip_code = gluecodes.parfillrightskip +local indentskip_code = gluecodes.indentskip local nodepool = nuts.pool @@ -128,19 +132,17 @@ local setmetatableindex = table.setmetatableindex local magicconstants = tex.magicconstants local running = magicconstants.running --- - local striprange = nuts.striprange local processwords = nuts.processwords --- +local setcoloring = nuts.colors.set -local rules = nodes.rules or { } -nodes.rules = rules -rules.data = rules.data or { } +local rules = nodes.rules or { } +nodes.rules = rules +rules.data = rules.data or { } -local nutrules = nuts.rules or { } -nuts.rules = nutrules -- not that many +local nutrules = nuts.rules or { } +nuts.rules = nutrules -- not that many storage.register("nodes/rules/data", rules.data, "nodes.rules.data") @@ -211,8 +213,8 @@ local subtypeactions = { [rulecodes.radical] = mathradical, } -local function process_rule(n,h,v) - local n = tonut(n) +function rules.process(n,h,v) + local n = tonut(n) -- already a nut local s = getsubtype(n) local a = subtypeactions[s] if a then @@ -220,10 +222,6 @@ local function process_rule(n,h,v) end end -callbacks.register("process_rule",process_rule,"handle additional user rule features") - -callbacks.functions.process_rule = process_rule - -- local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end) @@ -367,20 +365,9 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a else for i=1,level do local hd = (offset+(i-1)*dy)*e - m --- local ht = hd + rulethickness - m --- local dp = -hd + rulethickness + m local ht = hd + rulethickness local dp = -hd + rulethickness - local r = new_rule(wd,ht,dp) - -- can be done more efficient - if color then - setattr(r,a_colormodel,colorspace) - setattr(r,a_color,color) - end - if transparency then - setattr(r,a_transparency,transparency) - end - inject(r,wd,ht,dp) + inject(setcoloring(new_rule(wd,ht,dp),colorspace,color,transparency),wd,ht,dp) end end end @@ -489,15 +476,7 @@ local function linefiller(current,data,width,location) direction = getdirection(current), } else - local rule = new_rule(width,height,depth) - if ca then - setattr(rule,a_colorspace,ma) - setattr(rule,a_color,ca) - end - if ta then - setattr(rule,a_transparency,ta) - end - return rule + return setcoloring(new_rule(width,height,depth),ma,ca,ta) end end @@ -525,35 +504,39 @@ function linefillers.filler(current,data,width,height,depth) direction = getdirection(current), } else - local rule = new_rule(width,height,depth) - if ca then - setattr(rule,a_colorspace,ma) - setattr(rule,a_color,ca) - end - if ta then - setattr(rule,a_transparency,ta) - end - return rule + return setcoloring(new_rule(width,height,depth),ma,ca,ta) end end end end -local function find_attr(head,attr) - while head do - local a = head[attr] - if a then - return a, head +local function getskips(list) -- this could be a helper + local ls = nil + local rs = nil + local is = nil + local pl = nil + local pr = nil + local ok = false + for n, subtype in nextglue, list do + if subtype == rightskip_code then + rs = n + elseif subtype == parfillrightskip_code then + pr = n + elseif subtype == leftskip_code then + ls = n + elseif subtype == indentskip_code then + is = n + elseif subtype == parfillleftskip_code then + pl = n end - head = getnext(head) end + return is, ls, pl, pr, rs end function linefillers.handler(head) - for current, subtype in nexthlist, head do - if current and subtype == linelist_code then - -- why doesn't leftskip take the attributes - -- or list[linefiller] or maybe first match (maybe we need a fast helper for that) + -- we have a normalized line .. + for current, id, subtype, list in nextlist, head do + if subtype == linelist_code and list then local a = getattr(current,a_linefiller) if a then local class = a % 1000 @@ -575,96 +558,44 @@ function linefillers.handler(head) rightlocal = true end -- - local list = getlist(current) - -- - if location == v_left or location == v_both then - local lskip = nil -- leftskip - local iskip = nil -- indentation - local head = list - while head do - local id = getid(head) - if id == glue_code then - if getsubtype(head) == leftskip_code then - lskip = head - else - break - end - elseif id == par_code or id == dir_code then - -- go on - elseif id == hlist_code then - if getsubtype(head) == indentlist_code then - iskip = head - end - break - else - break - end - head = getnext(head) - end - if head then - local indentation = iskip and getwidth(iskip) or 0 - local leftfixed = lskip and getwidth(lskip) or 0 - local lefttotal = lskip and effective_glue(lskip,current) or 0 + local is, ls, pl, pr, rs = getskips(list) + if ls and rs then + if location == v_left or location == v_both then + local indentation = is and getwidth(is) or 0 + local leftfixed = ls and getwidth(ls) or 0 + local lefttotal = ls and effective_glue(ls,current) or 0 local width = lefttotal - (leftlocal and leftfixed or 0) + indentation - distance if width > threshold then - if iskip then - setwidth(iskip,0) + if is then + setwidth(is,0) end - if lskip then - setglue(lskip,leftlocal and getwidth(lskip) or nil) - if distance > 0 then - insert_node_after(list,lskip,new_kern(distance)) - end - insert_node_after(list,lskip,linefiller(current,data,width,"left")) - else - insert_node_before(list,head,linefiller(current,data,width,"left")) - if distance > 0 then - insert_node_before(list,head,new_kern(distance)) - end + setglue(ls,leftlocal and getwidth(ls) or nil) + if distance > 0 then + insert_node_after(list,ls,new_kern(distance)) end + insert_node_after(list,ls,linefiller(current,data,width,"left")) end end - end - -- - if location == v_right or location == v_both then - local pskip = nil -- parfillskip - local rskip = nil -- rightskip - local tail = find_tail(list) - while tail and getid(tail) == glue_code do - local subtype = getsubtype(tail) - if subtype == rightskip_code then - rskip = tail - elseif subtype == parfillskip_code then - pskip = tail - else - break - end - tail = getprev(tail) - end - if tail then - local rightfixed = rskip and getwidth(rskip) or 0 - local righttotal = rskip and effective_glue(rskip,current) or 0 - local parfixed = pskip and getwidth(pskip) or 0 - local partotal = pskip and effective_glue(pskip,current) or 0 + -- + if location == v_right or location == v_both then + local rightfixed = rs and getwidth(rs) or 0 + local righttotal = rs and effective_glue(rs,current) or 0 + local parfixed = pr and getwidth(pr) or 0 + local partotal = pr and effective_glue(pr,current) or 0 local width = righttotal - (rightlocal and rightfixed or 0) + partotal - distance if width > threshold then - if pskip then - setglue(pskip) + if pr then + setglue(pr) end - if rskip then - setglue(rskip,rightlocal and getwidth(rskip) or nil) - if distance > 0 then - insert_node_before(list,rskip,new_kern(distance)) - end - insert_node_before(list,rskip,linefiller(current,data,width,"right")) - else - insert_node_after(list,tail,linefiller(current,data,width,"right")) - if distance > 0 then - insert_node_after(list,tail,new_kern(distance)) - end + setglue(rs,rightlocal and getwidth(rs) or nil) + if distance > 0 then + insert_node_before(list,rs,new_kern(distance)) end + insert_node_before(list,rs,linefiller(current,data,width,"right")) end end + else + -- error, not a properly normalized line end end end diff --git a/tex/context/base/mkxl/node-rul.mkxl b/tex/context/base/mkxl/node-rul.mkxl index a23867804..c507885a8 100644 --- a/tex/context/base/mkxl/node-rul.mkxl +++ b/tex/context/base/mkxl/node-rul.mkxl @@ -419,9 +419,6 @@ {\removeunwantedspaces \endgroup} -% \protected\def\node_shifts_direct#1% -% {\doisolatedgroupedalign{\node_shifts_set{#1}}\donothing} - \protected\def\node_shifts_direct#1% {\groupedcommand {\begingroup\dostartisolation\begingroup\node_shifts_set{#1}\ignorespaces} @@ -533,7 +530,7 @@ {\begingroup \par \def\currentlinefiller{#1}% - \ifargument#2\or + \ifparameter#2\or % we need to update settings \setuplinefiller[#1][#2]% no \setupcurrentlinefiller as we need to update settings \fi diff --git a/tex/context/base/mkxl/node-tra.lmt b/tex/context/base/mkxl/node-tra.lmt new file mode 100644 index 000000000..7b401361f --- /dev/null +++ b/tex/context/base/mkxl/node-tra.lmt @@ -0,0 +1,734 @@ +if not modules then modules = { } end modules ['node-tra'] = { + version = 1.001, + comment = "companion to node-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +

This is rather experimental. We need more control and some of this +might become a runtime module instead. This module will be cleaned up!

+--ldx]]-- + +local next = next +local utfchar = utf.char +local format, match, gmatch, concat, rep = string.format, string.match, string.gmatch, table.concat, string.rep +local lpegmatch = lpeg.match +local clock = os.gettimeofday or os.clock -- should go in environment + +local report_nodes = logs.reporter("nodes","tracing") + +local nodes, node, context = nodes, node, context + +local texgetattribute = tex.getattribute + +local tracers = nodes.tracers or { } +nodes.tracers = tracers + +local tasks = nodes.tasks or { } +nodes.tasks = tasks + +local handlers = nodes.handlers or {} +nodes.handlers = handlers + +local injections = nodes.injections or { } +nodes.injections = injections + +local nuts = nodes.nuts +local tonut = nuts.tonut +local tonode = nuts.tonode + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local getdisc = nuts.getdisc +local setattr = nuts.setattr +local setattrs = nuts.setattrs +local getglue = nuts.getglue +local isglyph = nuts.isglyph +local getdirection = nuts.getdirection +local getwidth = nuts.getwidth + +local flush_list = nuts.flush_list +local count_nodes = nuts.countall +local used_nodes = nuts.usedlist + +local nextnode = nuts.traversers.node +local nextglyph = nuts.traversers.glyph + +local d_tostring = nuts.tostring + +local nutpool = nuts.pool +local new_rule = nutpool.rule + +local nodecodes = nodes.nodecodes +local whatsitcodes = nodes.whatsitcodes +local fillcodes = nodes.fillcodes + +local subtypes = nodes.subtypes + +local glyph_code = nodecodes.glyph +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local disc_code = nodecodes.disc +local glue_code = nodecodes.glue +local kern_code = nodecodes.kern +local rule_code = nodecodes.rule +local dir_code = nodecodes.dir +local par_code = nodecodes.par +local whatsit_code = nodecodes.whatsit + +local dimenfactors = number.dimenfactors +local formatters = string.formatters + +local start_of_par = nuts.start_of_par + +-- this will be reorganized: + +function nodes.showlist(head, message) + if message then + report_nodes(message) + end + for n in nextnode, tonut(head) do + report_nodes(d_tostring(n)) + end +end + +function nodes.handlers.checkglyphs(head,message) + local h = tonut(head) -- tonut needed? + local t = { } + local n = 0 + local f = formatters["%U:%s"] + for g, char, font in nextglyph, h do + n = n + 1 + t[n] = f(char,getsubtype(g)) + end + if n == 0 then + -- nothing to report + elseif message and message ~= "" then + report_nodes("%s, %s glyphs: % t",message,n,t) + else + report_nodes("%s glyphs: % t",n,t) + end + return false +end + +local fontcharacters -- = fonts.hashes.descriptions + +local function tosequence(start,stop,compact) + if start then + if not fontcharacters then + fontcharacters = fonts.hashes.descriptions + if not fontcharacters then + return "[no char data]" + end + end + local f_sequence = formatters["U+%04X:%s"] + local f_subrange = formatters["[[ %s ][ %s ][ %s ]]"] + start = tonut(start) + stop = stop and tonut(stop) + local t = { } + local n = 0 + while start do + local c, id = isglyph(start) + if c then + local u = fontcharacters[id][c] -- id == font id + u = u and u.unicode or c + if type(u) == "table" then + local tt = { } + for i=1,#u do + local c = u[i] + tt[i] = compact and utfchar(c) or f_sequence(c,utfchar(c)) + end + n = n + 1 ; t[n] = "(" .. concat(tt," ") .. ")" + else + n = n + 1 ; t[n] = compact and utfchar(c) or f_sequence(c,utfchar(c)) + end + elseif id == disc_code then + local pre, post, replace = getdisc(start) + t[#t+1] = f_subrange(pre and tosequence(pre),post and tosequence(post),replace and tosequence(replace)) + elseif id == rule_code then + n = n + 1 ; t[n] = compact and "|" or nodecodes[id] or "?" + elseif id == dir_code then + local d, p = getdirection(start) + n = n + 1 ; t[n] = "[<" .. (p and "-" or "+") .. d .. ">]" -- todo l2r etc + elseif id == par_code and start_of_par(current) then + n = n + 1 ; t[n] = "[<" .. getdirection(start) .. ">]" -- todo l2r etc + elseif compact then + n = n + 1 ; t[n] = "[]" + else + n = n + 1 ; t[n] = nodecodes[id] + end + if start == stop then + break + else + start = getnext(start) + end + end + if compact then + return concat(t) + else + return concat(t," ") + end + else + return "[empty]" + end +end + +nodes.tosequence = tosequence +nuts .tosequence = tosequence + +if CONTEXTLMTXMODE > 0 then + + function nodes.report(t) + report_nodes("output %a, %s nodes",tex.getoutputactive(),count_nodes(t)) + end + +else + + function nodes.report(t) + report_nodes("output %a, %s nodes",status.output_active,count_nodes(t)) + end + +end + +function nodes.packlist(head) + local t = { } + for n in nextnode, tonut(head) do + t[#t+1] = d_tostring(n) + end + return t +end + +function nodes.idstostring(head,tail) + head = tonut(head) + tail = tail and tonut(tail) + local t = { } + local last_id = nil + local last_n = 0 + local f_two = formatters["[%s*%s]"] + local f_one = formatters["[%s]"] + for n, id, subtype in nextnode, head do + if id == whatsit_code then + id = whatsitcodes[subtype] + else + id = nodecodes[id] + end + if not last_id then + last_id = id + last_n = 1 + elseif last_id == id then + last_n = last_n + 1 + else + if last_n > 1 then + t[#t+1] = f_two(last_n,last_id) + else + t[#t+1] = f_one(last_id) + end + last_id = id + last_n = 1 + end + if n == tail then + break + end + end + if not last_id then + t[#t+1] = "no nodes" + else + if last_n > 1 then + t[#t+1] = f_two(last_n,last_id) + else + t[#t+1] = f_one(last_id) + end + end + return concat(t," ") +end + +function nodes.idsandsubtypes(head) + local h = tonut(head) + local t = { } + local f = formatters["%s:%s"] + for n, id, subtype in nextnode, h do + local c = nodecodes[id] + if subtype then + t[#t+1] = f(c,subtypes[id][subtype]) + else + t[#t+1] = c + end + end + return concat(t, " ") +end + +-- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks +-- head = tonut(head) +-- tail = tonut(tail) +-- local n = head +-- while n.next do +-- n = n.next +-- end +-- local t, last_id, last_n = { }, nil, 0 +-- while n do +-- local id = n.id +-- if not last_id then +-- last_id, last_n = id, 1 +-- elseif last_id == id then +-- last_n = last_n + 1 +-- else +-- if last_n > 1 then +-- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") +-- else +-- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") +-- end +-- last_id, last_n = id, 1 +-- end +-- if n == head then +-- break +-- end +-- n = getprev(n) +-- end +-- if not last_id then +-- t[#t+1] = "no nodes" +-- elseif last_n > 1 then +-- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") +-- else +-- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") +-- end +-- return table.concat(table.reversed(t)," ") +-- end + +local function showsimplelist(h,depth,n) + h = h and tonut(h) + while h do + report_nodes("% w%s",n,d_tostring(h)) + if not depth or n < depth then + local id = getid(h) + if id == hlist_code or id == vlist_code then + showsimplelist(getlist(h),depth,n+1) + end + end + h = getnext(h) + end +end + +nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end + +local function listtoutf(h,joiner,textonly,last,nodisc) + local w = { } + local n = 0 + local g = formatters["<%i>"] + local d = formatters["[%s|%s|%s]"] + while h do + local c, id = isglyph(h) + if c then + n = n + 1 ; w[n] = c >= 0 and utfchar(c) or g(c) + if joiner then + n = n + 1 ; w[n] = joiner + end + elseif id == disc_code then + local pre, pos, rep = getdisc(h) + if not nodisc then + n = n + 1 ; w[n] = d( + pre and listtoutf(pre,joiner,textonly) or "", + pos and listtoutf(pos,joiner,textonly) or "", + rep and listtoutf(rep,joiner,textonly) or "" + ) + elseif rep then + n = n + 1 ; w[n] = listtoutf(rep,joiner,textonly) or "" + end + if joiner then + n = n + 1 ; w[n] = joiner + end + elseif textonly then + if id == glue_code then + if getwidth(h) > 0 then + n = n + 1 ; w[n] = " " + end + elseif id == hlist_code or id == vlist_code then + n = n + 1 ; w[n] = "[" + n = n + 1 ; w[n] = listtoutf(getlist(h),joiner,textonly,last,nodisc) + n = n + 1 ; w[n] = "]" + end + else + n = n + 1 ; w[n] = "[-]" + end + if h == last then + break + else + h = getnext(h) + end + end + return concat(w,"",1,(w[n] == joiner) and (n-1) or n) +end + +function nodes.listtoutf(h,joiner,textonly,last,nodisc) + if h then + local joiner = joiner == true and utfchar(0x200C) or joiner -- zwnj + return listtoutf(tonut(h),joiner,textonly,last and tonut(last),nodisc) + else + return "" + end +end + +local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" } + +local function showboxes(n,symbol,depth) + depth = depth or 0 + symbol = symbol or "." + for n, id, subtype in nextnode, tonut(n) do + if id == hlist_code or id == vlist_code then + report_nodes(rep(symbol,depth) .. what[subtype] or subtype) + showboxes(getlist(n),symbol,depth+1) + end + end +end + +nodes.showboxes = showboxes + +local ptfactor = dimenfactors.pt +local bpfactor = dimenfactors.bp +local stripper = lpeg.patterns.stripzeros + +local f_f_f = formatters["%0.5Fpt plus %0.5F%s minus %0.5F%s"] +local f_f_m = formatters["%0.5Fpt plus %0.5F%s minus %0.5Fpt"] +local f_p_f = formatters["%0.5Fpt plus %0.5Fpt minus %0.5F%s"] +local f_p_m = formatters["%0.5Fpt plus %0.5Fpt minus %0.5Fpt"] +local f_f_z = formatters["%0.5Fpt plus %0.5F%s"] +local f_p_z = formatters["%0.5Fpt plus %0.5Fpt"] +local f_z_f = formatters["%0.5Fpt minus %0.5F%s"] +local f_z_m = formatters["%0.5Fpt minus %0.5Fpt"] +local f_z_z = formatters["%0.5Fpt"] + +local tonut = nodes.tonut + +local function nodetodimen(n) + n = tonut(n) + local id = getid(n) + if id == kern_code then + local width = getwidth(n) + if width == 0 then + return "0pt" + else + return f_z_z(width) + end + elseif id ~= glue_code then + return "0pt" + end + local width, stretch, shrink, stretch_order, shrink_order = getglue(n) + stretch = stretch / 65536 + shrink = shrink / 65536 + width = width / 65536 + if stretch_order ~= 0 then + if shrink_order ~= 0 then + return f_f_f(width,stretch,fillcodes[stretch_order],shrink,fillcodes[shrink_order]) + elseif shrink ~= 0 then + return f_f_m(width,stretch,fillcodes[stretch_order],shrink) + else + return f_f_z(width,stretch,fillcodes[stretch_order]) + end + elseif shrink_order ~= 0 then + if stretch ~= 0 then + return f_p_f(width,stretch,shrink,fillcodes[shrink_order]) + else + return f_z_f(width,shrink,fillcodes[shrink_order]) + end + elseif stretch ~= 0 then + if shrink ~= 0 then + return f_p_m(width,stretch,shrink) + else + return f_p_z(width,stretch) + end + elseif shrink ~= 0 then + return f_z_m(width,shrink) + elseif width == 0 then + return "0pt" + else + return f_z_z(width) + end +end + + +-- number.todimen(123) +-- number.todimen(123,"cm") +-- number.todimen(123,false,"%F)) + +local f_pt = formatters["%p"] +local f_un = formatters["%F%s"] + +dimenfactors[""] = dimenfactors.pt + +local function numbertodimen(d,unit,fmt) + if not d or d == 0 then + if fmt then + return formatters[fmt](0,unit or "pt") + elseif unit then + return 0 .. unit + else + return "0pt" + end + elseif fmt then + if not unit then + unit = "pt" + end + return formatters[fmt](d*dimenfactors[unit],unit) + elseif not unit or unit == "pt" then + return f_pt(d) + else + return f_un(d*dimenfactors[unit],unit) + end +end + +number.todimen = numbertodimen +nodes .todimen = nodetodimen + +function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end +function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end +function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end +function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end +function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end +function number.toscaledpoints(n) return n .. "sp" end +function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end +function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end +function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end +function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end +-------- number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end +-------- number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end + +function nodes.topoints (n,fmt) return nodetodimen(n,"pt",fmt) end +function nodes.toinches (n,fmt) return nodetodimen(n,"in",fmt) end +function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end +function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end +function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end +function nodes.toscaledpoints(n) return n .. "sp" end +function nodes.tobasepoints (n,fmt) return nodetodimen(n,"bp",fmt) end +function nodes.topicas (n,fmt) return nodetodimen(n "pc",fmt) end +function nodes.todidots (n,fmt) return nodetodimen(n,"dd",fmt) end +function nodes.tociceros (n,fmt) return nodetodimen(n,"cc",fmt) end +-------- nodes.tonewdidots (n,fmt) return nodetodimen(n,"nd",fmt) end +-------- nodes.tonewciceros (n,fmt) return nodetodimen(n,"nc",fmt) end + +-- stop redefinition + +local points = function(n) + if not n or n == 0 then + return "0pt" + elseif type(n) == "number" then + return lpegmatch(stripper,format("%.5fpt",n*ptfactor)) -- faster than formatter + else + return numbertodimen(n,"pt") -- also deals with nodes + end +end + +local basepoints = function(n) + if not n or n == 0 then + return "0bp" + elseif type(n) == "number" then + return lpegmatch(stripper,format("%.5fbp",n*bpfactor)) -- faster than formatter + else + return numbertodimen(n,"bp") -- also deals with nodes + end +end + +local pts = function(n) + if not n or n == 0 then + return "0pt" + elseif type(n) == "number" then + return format("%.5fpt",n*ptfactor) -- faster than formatter + else + return numbertodimen(n,"pt") -- also deals with nodes + end +end + +local nopts = function(n) + if not n or n == 0 then + return "0" + else + return format("%.5f",n*ptfactor) -- faster than formatter + end +end + +number.points = points +number.basepoints = basepoints +number.pts = pts +number.nopts = nopts + +nodes.points = function(n) return numbertodimen(n,"pt") end +nodes.basepoints = function(n) return numbertodimen(n,"bp") end +nodes.pts = function(n) return numbertodimen(n,"pt") end +nodes.nopts = function(n) return format("%.5f",n*ptfactor) end + +local colors = { } +tracers.colors = colors + +local unsetvalue = attributes.unsetvalue + +local a_color = attributes.private('color') +local a_colormodel = attributes.private('colormodel') +local m_color = attributes.list[a_color] or { } + +function colors.set(n,c,s,t) -- also unsets ! + local mc = m_color[c] + local nn = tonut(n) + if mc then + local mm = s or texgetattribute(a_colormodel) + if mm <= 0 then + mm = 1 + end + -- only here t is dealt with + if t then + setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,m_transparency[t] or unsetvalue) + else + setattrs(nn,a_colormodel,mm,a_color,mc) + end + else + setattr(nn,a_color,unsetvalue) + end + return n +end + +function colors.setlist(n,c,s,t) + local nn = tonut(n) + local mc = m_color[c] or unsetvalue + local mm = s or texgetattribute(a_colormodel) + if mm <= 0 then + mm = 1 + end + if t then + t = m_transparency[t] or unsetvalue + while nn do + setattrs(nn,a_colormodel,mm,a_color,mc,a_transparency,t) + nn = getnext(nn) + end + else + while nn do + setattrs(nn,a_colormodel,mm,a_color,mc) + nn = getnext(nn) + end + end + return n +end + +function colors.reset(n) + setattr(tonut(n),a_color,unsetvalue) + return n +end + +-- maybe + +local transparencies = { } +tracers.transparencies = transparencies + +local a_transparency = attributes.private('transparency') +local m_transparency = attributes.list[a_transparency] or { } + +function transparencies.set(n,t) + setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue) + return n +end + +function transparencies.setlist(n,c,s) + local nn = tonut(n) + local mt = m_transparency[c] or unsetvalue + while nn do + setattr(nn,a_transparency,mt) + nn = getnext(nn) + end + return n +end + +function transparencies.reset(n) + setattr(n,a_transparency,unsetvalue) + return n +end + +-- for the moment here + +local visualizers = nodes.visualizers or { } +nodes.visualizers = visualizers + +function visualizers.handler(head) + return head, false +end + +-- we could cache attribute lists and set attr (copy will increment count) .. todo .. +-- although tracers are used seldom + +local function setproperties(n,c,s) + local nn = tonut(n) + local mm = texgetattribute(a_colormodel) + setattr(nn,a_colormodel,mm > 0 and mm or 1) + setattr(nn,a_color,m_color[c]) + setattr(nn,a_transparency,m_transparency[c]) + return n +end + +tracers.setproperties = setproperties + +-- setting attrlist entries instead of attr for successive entries doesn't +-- speed up much (this function is only used in tracers anyway) + +function tracers.setlist(n,c,s) + local nn = tonut(n) + local mc = m_color[c] + local mt = m_transparency[c] + local mm = texgetattribute(a_colormodel) + if mm <= 0 then + mm = 1 + end + while nn do + setattr(nn,a_colormodel,mm) + setattr(nn,a_color,mc) + setattr(nn,a_transparency,mt) + nn = getnext(nn) + end + return n +end + +function tracers.resetproperties(n) + local nn = tonut(n) + setattr(nn,a_color,unsetvalue) + setattr(nn,a_transparency,unsetvalue) + return n +end + +-- this one returns a nut + +local nodestracerpool = { } +local nutstracerpool = { } + +tracers.pool = { + nodes = nodestracerpool, + nuts = nutstracerpool, +} + +table.setmetatableindex(nodestracerpool,function(t,k,v) + local f = nutstracerpool[k] + local v = function(...) + return tonode(f(...)) + end + t[k] = v + return v +end) + +function nutstracerpool.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup) + return setproperties(new_rule(w,h,d),c,s) +end + +tracers.rule = nodestracerpool.rule -- for a while + +-- local function show(head,n,message) +-- print("START",message or "") +-- local i = 0 +-- for current in traverse(head) do +-- local prev = getprev(current) +-- local next = getnext(current) +-- i = i + 1 +-- print(i, prev and nodecodes[getid(prev)],nodecodes[getid(current)],next and nodecodes[getid(next)]) +-- if i == n then +-- break +-- end +-- end +-- print("STOP", message or "") +-- end diff --git a/tex/context/base/mkxl/typo-drp.lmt b/tex/context/base/mkxl/typo-drp.lmt new file mode 100644 index 000000000..8741376b6 --- /dev/null +++ b/tex/context/base/mkxl/typo-drp.lmt @@ -0,0 +1,346 @@ +if not modules then modules = { } end modules ['typo-drp'] = { + version = 1.001, + comment = "companion to typo-drp.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This one is sensitive for order (e.g. when combined with first line +-- processing. + +-- todo: use isglyph + +local tonumber, type, next = tonumber, type, next +local ceil = math.ceil +local settings_to_hash = utilities.parsers.settings_to_hash + +local trace_initials = false trackers.register("typesetters.initials", function(v) trace_initials = v end) +local report_initials = logs.reporter("nodes","initials") + +local initials = typesetters.paragraphs or { } +typesetters.initials = initials or { } + +local nodes = nodes + +local tasks = nodes.tasks +local enableaction = tasks.enableaction +local disableaction = tasks.disableaction + +local nuts = nodes.nuts +local tonut = nodes.tonut + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getchar = nuts.getchar +local getid = nuts.getid +local getattr = nuts.getattr +local getwhd = nuts.getwhd + +local getprop = nuts.getprop +local setprop = nuts.setprop + +local setlink = nuts.setlink +local setprev = nuts.setprev +local setnext = nuts.setnext +local setfont = nuts.setfont +local setchar = nuts.setchar +local setwhd = nuts.setwhd +local setkern = nuts.setkern +local setoffsets = nuts.setoffsets +local setglyphdata = nuts.setglyphdata + +local hpack_nodes = nuts.hpack + +local nodecodes = nodes.nodecodes + +local nodepool = nuts.pool +local new_kern = nodepool.kern + +local insert_before = nuts.insert_before +local insert_after = nuts.insert_after +local remove_node = nuts.remove +local start_of_par = nuts.start_of_par + +local nextnode = nuts.traversers.node +local nextglyph = nuts.traversers.glyph + +local setcoloring = nuts.colors.set + +local variables = interfaces.variables +local v_default = variables.default +local v_margin = variables.margin +local v_auto = variables.auto +local v_first = variables.first +local v_last = variables.last + +local texget = tex.get +local texset = tex.set +local unsetvalue = attributes.unsetvalue + +local glyph_code = nodecodes.glyph +local hlist_code = nodecodes.hlist +local glue_code = nodecodes.glue +local kern_code = nodecodes.kern +local par_code = nodecodes.par + +local actions = { } +initials.actions = actions + +local a_initial = attributes.private("initial") + +local category = characters.category + +local function set(par,specification) + enableaction("processors","typesetters.initials.handler") + if trace_initials then + report_initials("enabling initials") + end + setprop(par,a_initial,specification) +end + +function initials.set(specification) + nuts.setparproperty(set,specification) +end + +interfaces.implement { + name = "setinitial", + actions = initials.set, + arguments = { + { + { "location" }, + { "enabled", "boolean" }, + { "method" }, + { "distance" ,"dimen" }, + { "hoffset" ,"dimen" }, + { "voffset" ,"dimen" }, + { "font", "integer" }, + { "dynamic", "integer" }, + { "ca", "integer" }, + { "ma", "integer" }, + { "ta", "integer" }, + { "n", "integer" }, + { "m", "integer" }, + } + } +} + +-- todo: prevent linebreak .. but normally a initial ends up at the top of +-- a page so this has a low priority + +actions[v_default] = function(head,setting) + -- begin of par + local first = getnext(head) + local indent = false + -- parbox .. needs to be set at 0 + if first and getid(first) == hlist_code then + first = getnext(first) + indent = true + end + -- we need to skip over kerns and glues (signals) + while first and getid(first) ~= glyph_code do + first = getnext(first) + end + if first and getid(first) == glyph_code then + local ma = setting.ma or 0 + local ca = setting.ca + local ta = setting.ta + local last = first + local distance = setting.distance or 0 + local voffset = setting.voffset or 0 + local hoffset = setting.hoffset or 0 + local parindent = texget("parindent") + local baseline = texget("baselineskip",false) + local lines = tonumber(setting.n) or 0 + local dynamic = setting.dynamic + local font = setting.font + local method = settings_to_hash(setting.method) + local length = tonumber(setting.m) or 1 + -- + -- 1 char | n chars | skip first quote | ignore punct | keep punct + -- + if getattr(first,a_initial) then + for current in nextnode, getnext(first) do + if getattr(current,a_initial) then + last = current + else + break + end + end + elseif method[v_auto] then + local char = getchar(first) + local kind = category(char) + if kind == "po" or kind == "pi" then + if method[v_first] then + -- remove quote etc before initial + local next = getnext(first) + if not next then + -- don't start with a quote or so + return head + end + last = nil + for current in nextglyph, next do + head, first = remove_node(head,first,true) + first = current + last = first + break + end + if not last then + -- no following glyph or so + return head + end + else + -- keep quote etc with initial + local next = getnext(first) + if not next then + -- don't start with a quote or so + return head + end + for current in nextglyph, next do + last = current + break + end + if last == first then + return head + end + end + elseif kind == "pf" then + -- error: final quote + else + -- okay + end + -- maybe also: get all A. B. etc + local next = getnext(first) + if next then + for current, char in nextglyph, next do + local kind = category(char) + if kind == "po" then + if method[v_last] then + -- remove period etc after initial + remove_node(head,current,true) + else + -- keep period etc with initial + last = current + end + end + break + end + end + else + for current in nextglyph, first do + last = current + if length <= 1 then + break + else + length = length - 1 + end + end + end + local current = first + while true do + local id = getid(current) + if id == kern_code then + setkern(current,0) + elseif id == glyph_code then + local next = getnext(current) + if font then + setfont(current,font) + end + if dynamic > 0 then + setglyphdata(current,dynamic) + end + setcoloring(ma,ca,ta) + end + if current == last then + break + else + current = getnext(current) + end + end + -- We pack so that successive handling cannot touch the dropped cap. Packaging + -- in a hlist is also needed because we cannot locally adapt e.g. parindent (not + -- yet stored in with par). + local prev = getprev(first) + local next = getnext(last) + -- + setprev(first) + setnext(last) + local dropper = hpack_nodes(first) + local width, height, depth = getwhd(dropper) + setwhd(dropper,0,0,0) + -- + setlink(prev,dropper) + setlink(dropper,next) + -- + if next then + local current = next + while current do + local id = getid(current) + if id == glue_code or id == kern_code then + local next = getnext(current) + -- remove_node(current,current,true) -- created an invalid next link and dangling remains + remove_node(head,current,true) + current = next + else + break + end + end + end + -- + local hoffset = width + hoffset + distance + (indent and parindent or 0) + for current in nextglyph, first do + setoffsets(current,-hoffset,-voffset) -- no longer - height here + if current == last then + break + end + end + -- + first = dropper + -- + if setting.location == v_margin then + -- okay + else + if lines == 0 then -- safeguard, not too precise + lines = ceil((height+voffset) / baseline) + end + -- We cannot set parshape yet ... when we can I'll add a slope + -- option (positive and negative, in emwidth). + local hangafter = - lines + local hangindent = width + distance + if trace_initials then + report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent) + end + if CONTEXTLMTXMODE > 0 then + texset("hangafter",hangafter,true) + texset("hangindent",hangindent,true) + else + texset("hangafter",hangafter) + texset("hangindent",hangindent) + end + end + if indent then + insert_after(first,first,new_kern(-parindent)) + end + end + return head +end + +-- we can count ... when all done, we can disable ... + +function initials.handler(head) + if getid(head) == par_code and start_of_par(head) then + local settings = getprop(head,a_initial) + if settings then + disableaction("processors","typesetters.initials.handler") + local alternative = settings.alternative or v_default + local action = actions[alternative] or actions[v_default] + if action then + if trace_initials then + report_initials("processing initials, alternative %a",alternative) + end + return action(head,settings) + end + end + end + return head +end diff --git a/tex/context/base/mkxl/typo-drp.mkxl b/tex/context/base/mkxl/typo-drp.mkxl index 9a6ae7603..94e08c9b0 100644 --- a/tex/context/base/mkxl/typo-drp.mkxl +++ b/tex/context/base/mkxl/typo-drp.mkxl @@ -17,7 +17,7 @@ \unprotect -\registerctxluafile{typo-drp}{} +\registerctxluafile{typo-drp}{autosuffix} \definesystemattribute[initial][public] diff --git a/tex/context/base/mkxl/typo-fln.lmt b/tex/context/base/mkxl/typo-fln.lmt new file mode 100644 index 000000000..fa507df4b --- /dev/null +++ b/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" } +} diff --git a/tex/context/base/mkxl/typo-fln.mkxl b/tex/context/base/mkxl/typo-fln.mkxl index 2696dd06b..3ef688bb5 100644 --- a/tex/context/base/mkxl/typo-fln.mkxl +++ b/tex/context/base/mkxl/typo-fln.mkxl @@ -50,7 +50,7 @@ \unprotect -\registerctxluafile{typo-fln}{} +\registerctxluafile{typo-fln}{autosuffix} \definesystemattribute[firstline][public] diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 9515034ab..7fb03eb1e 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 2020-12-04 20:19 +-- merge date : 2020-12-06 18:12 do -- begin closure to overcome local limits and interference -- cgit v1.2.3