diff options
Diffstat (limited to 'tex')
49 files changed, 4778 insertions, 312 deletions
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 1f3cca5b3..ec7703565 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.01 17:48} +\newcontextversion{2020.12.03 18:56} %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 3bdad465e..8ffceb405 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.01 17:48} +\edef\contextversion{2020.12.03 18:56} %D For those who want to use this: diff --git a/tex/context/base/mkiv/back-exp.lua b/tex/context/base/mkiv/back-exp.lua index 314d66eeb..983c6059f 100644 --- a/tex/context/base/mkiv/back-exp.lua +++ b/tex/context/base/mkiv/back-exp.lua @@ -552,7 +552,7 @@ do if not less_state then setattribute(di,"file",tex.jobname) if included.date then - setattribute(di,"date",backends.timestamp()) + setattribute(di,"date",os.fulltime()) end setattribute(di,"context",environment.version) setattribute(di,"version",exportversion) @@ -3472,7 +3472,7 @@ local xmlpreamble = [[ return replacetemplate(xmlpreamble, { standalone = standalone and "yes" or "no", filename = tex.jobname, - date = included.date and backends.timestamp(), + date = included.date and os.fulltime(), contextversion = environment.version, exportversion = exportversion, }) diff --git a/tex/context/base/mkiv/back-ini.lua b/tex/context/base/mkiv/back-ini.lua index ca6f6c1e5..d01be4a73 100644 --- a/tex/context/base/mkiv/back-ini.lua +++ b/tex/context/base/mkiv/back-ini.lua @@ -62,17 +62,6 @@ backends.tables = { } setmetatableindex(backends.tables, tables backends.current = "unknown" -local lmtx_mode = nil - -local function lmtxmode() - if lmtx_mode == nil then - lmtx_mode = CONTEXTLMTXMODE > 0 and drivers and drivers.lmtxversion - end - return lmtx_mode -end - -codeinjections.lmtxmode = lmtxmode - function backends.install(what) if type(what) == "string" then local backend = backends[what] @@ -100,13 +89,7 @@ end statistics.register("used backend", function() local bc = backends.current if bc ~= "unknown" then - local lmtx = lmtxmode() - local cmnt = backends[bc].comment or "no comment" - if lmtx then - return format("lmtx version %0.2f, %s (%s)",lmtx,bc,cmnt) - else - return format("%s (%s)",bc,cmnt) - end + return format("%s (%s)",bc,backends[bc].comment or "no comment") else return nil end @@ -164,10 +147,8 @@ function codeinjections.setpagedimensions(paperwidth,paperheight) if paperheight then paper_height = paperheight end - if not lmtxmode() then - texset("global","pageheight",paper_height) - texset("global","pagewidth", paper_width) - end + texset("global","pageheight",paper_height) + texset("global","pagewidth", paper_width) return paper_width, paper_height end @@ -178,11 +159,10 @@ end implement { name = "shipoutoffset", actions = function() - context(lmtxmode() and "0pt" or "-1in") -- the old tex offset + context("-1in") -- the old tex offset end } - local page_x_origin = 0 local page_y_origin = 0 diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index ee813411b..9483292ec 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.01 17:48} +\newcontextversion{2020.12.03 18:56} %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.lus b/tex/context/base/mkiv/context.lus deleted file mode 100644 index 29c9b8301..000000000 --- a/tex/context/base/mkiv/context.lus +++ /dev/null @@ -1,15 +0,0 @@ -if not modules then modules = { } end modules ['context'] = { - version = 1.001, - comment = "companion to context.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - ---[[<p>This table specifies what stub files are needed in order to create -the format. These files are loaded before the format is made so that we -bypass kpse. When the format itself is used, another stub is used (with -suffix lui). The current format builder is to a large part determined by -the way luatex evolved and the process will probaby change.</p>]]-- - -return "luat-cod.lua" diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 5b566f070..0ae1b5027 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.01 17:48} +\edef\contextversion{2020.12.03 18:56} %D Kind of special: diff --git a/tex/context/base/mkiv/driv-ini.lua b/tex/context/base/mkiv/driv-ini.lua index 316613b21..1e19fd5f8 100644 --- a/tex/context/base/mkiv/driv-ini.lua +++ b/tex/context/base/mkiv/driv-ini.lua @@ -6,6 +6,10 @@ if not modules then modules = { } end modules ['driv-ini'] = { license = "see context related readme files" } +-- This file is a bit overkill because it was meant for both luatex and luametatex +-- but after the splti I decided not to simplify this one. It can only introduce +-- issues. + local type = type local addsuffix = file.addsuffix diff --git a/tex/context/base/mkiv/lpdf-mis.lua b/tex/context/base/mkiv/lpdf-mis.lua index a3f9789c8..94e9fc271 100644 --- a/tex/context/base/mkiv/lpdf-mis.lua +++ b/tex/context/base/mkiv/lpdf-mis.lua @@ -244,12 +244,6 @@ local function setupidentity() addtoinfo("ID", pdfstring(id), id) -- needed for pdf/x -- addtoinfo("ConTeXt.Version",version) - -- - local lmtx = codeinjections.lmtxmode() - if lmtx then - addtoinfo("ConTeXt.LMTX",formatters["%0.2f"](lmtx)) - end - -- addtoinfo("ConTeXt.Time",os.date("%Y-%m-%d %H:%M")) addtoinfo("ConTeXt.Jobname",jobname) addtoinfo("ConTeXt.Url","www.pragma-ade.com") diff --git a/tex/context/base/mkiv/luat-fmt.lua b/tex/context/base/mkiv/luat-fmt.lua index 30792931e..eb24378a4 100644 --- a/tex/context/base/mkiv/luat-fmt.lua +++ b/tex/context/base/mkiv/luat-fmt.lua @@ -11,7 +11,7 @@ if not modules then modules = { } end modules ['luat-fmt'] = { -- For instance, in the real beginning we had runtime loading because we had no -- bytecode registers yet. We also had multiple files as stubs and the context.lus -- file specified these. More than a decade only the third method was used, just --- loading luat-cod, so in the end we cpould get rid of the lus file. In due time +-- loading luat-cod, so in the end we could get rid of the lus file. In due time -- I'll strip the code here because something generic will never take of and we -- moved on to luametatex anyway. diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex ea407d533..b6aed6495 100644 --- a/tex/context/base/mkiv/status-files.pdf +++ b/tex/context/base/mkiv/status-files.pdf diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differindex 97cefc1a8..29161ec70 100644 --- a/tex/context/base/mkiv/status-lua.pdf +++ b/tex/context/base/mkiv/status-lua.pdf diff --git a/tex/context/base/mkiv/task-ini.lua b/tex/context/base/mkiv/task-ini.lua index 45c0c3239..323ad4dc9 100644 --- a/tex/context/base/mkiv/task-ini.lua +++ b/tex/context/base/mkiv/task-ini.lua @@ -123,7 +123,12 @@ appendaction("math", "builders", "typesetters.directions.processmath" appendaction("math", "builders", "noads.handlers.makeup", nil, "nonut", "disabled" ) appendaction("math", "builders", "noads.handlers.align", nil, "nonut", "enabled" ) +if CONTEXTLMTXMODE == 0 then + appendaction("finalizers", "lists", "typesetters.paragraphs.normalize", nil, "nut", "enabled" ) -- "disabled" + +end + appendaction("finalizers", "lists", "typesetters.margins.localhandler", nil, "nut", "disabled" ) appendaction("finalizers", "lists", "builders.paragraphs.keeptogether", nil, "nut", "disabled" ) appendaction("finalizers", "fonts", "builders.paragraphs.solutions.splitters.optimize", nil, "nonut", "disabled" ) diff --git a/tex/context/base/mkiv/type-ini.lua b/tex/context/base/mkiv/type-ini.lua index a2e19725e..771cf39c0 100644 --- a/tex/context/base/mkiv/type-ini.lua +++ b/tex/context/base/mkiv/type-ini.lua @@ -17,9 +17,6 @@ local implement = interfaces.implement local uselibrary = resolvers.uselibrary -local name_one = nil -local name_two = nil - local p_strip = Cs((P("type-") * (P("imp-")^0))^0/"" * P(1)^0) local report = logs.reporter("fonts","typescripts") @@ -29,6 +26,7 @@ local function action(name,foundname) end local patterns = { + "type-imp-%s.mkxl", "type-imp-%s.mkiv", "type-imp-%s.tex" } @@ -56,6 +54,7 @@ implement { } local patterns = { + "type-imp-%s.mkxl", "type-imp-%s.mkiv", "type-imp-%s.tex", -- obsolete @@ -63,6 +62,9 @@ local patterns = { "type-%s.tex" } +-- local name_one = nil +-- local name_two = nil +-- -- local function failure_two(name) -- report("unknown library %a or %a",name_one,name_two) -- end diff --git a/tex/context/base/mkiv/typo-lin.lua b/tex/context/base/mkiv/typo-lin.lua index 55fd08c5a..84d622e1e 100644 --- a/tex/context/base/mkiv/typo-lin.lua +++ b/tex/context/base/mkiv/typo-lin.lua @@ -484,7 +484,7 @@ function paragraphs.calculatedelta(n,width,delta,atleft,islocal,followshape,area addanchortoline(line,setanchor(number)) line.hanchor = true end - local blob = getposition(s_anchor,number) + local blob = getposition("md:h",number) if blob then local reference = getreserved(area,blob.c) if reference then diff --git a/tex/context/base/mkxl/back-ini.lmt b/tex/context/base/mkxl/back-ini.lmt new file mode 100644 index 000000000..ee40c384d --- /dev/null +++ b/tex/context/base/mkxl/back-ini.lmt @@ -0,0 +1,157 @@ +if not modules then modules = { } end modules ['back-ini'] = { + version = 1.001, + comment = "companion to back-ini.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 format = string.format + +backends = backends or { } +local backends = backends + +local context = context + +local trace = false trackers.register("backend", function(v) trace = v end) +local report = logs.reporter("backend") + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex +local setaction = nodes.tasks.setaction + +local implement = interfaces.implement +local variables = interfaces.variables + +local texset = tex.set + +local nodeinjections = { } +local codeinjections = { } +local registrations = { } +local tables = allocate() + +local function nothing() + return nil +end + +backends.nothing = nothing + +local function donothing(t,k) + t[k] = nothing + return nothing +end + +setmetatableindex(nodeinjections, donothing) +setmetatableindex(codeinjections, donothing) +setmetatableindex(registrations, donothing) + +local defaults = { + nodeinjections = nodeinjections, + codeinjections = codeinjections, + registrations = registrations, + tables = tables, +} + +backends.defaults = defaults + +backends.nodeinjections = { } setmetatableindex(backends.nodeinjections, nodeinjections) +backends.codeinjections = { } setmetatableindex(backends.codeinjections, codeinjections) +backends.registrations = { } setmetatableindex(backends.registrations, registrations) +backends.tables = { } setmetatableindex(backends.tables, tables) + +backends.current = "unknown" + +function backends.install(what) + if type(what) == "string" then + local backend = backends[what] + if backend then + if trace then + if backend.comment then + report("initializing backend %a, %a",what,backend.comment) + else + report("initializing backend %a",what) + end + end + backends.current = what + for category, default in next, defaults do + local target = backends[category] + local plugin = backend [category] + setmetatableindex(plugin, default) + setmetatableindex(target, plugin) + end + elseif trace then + report("no backend named %a",what) + end + end +end + +statistics.register("used backend", function() + local bc = backends.current + if bc ~= "unknown" then + return format("%s (%s)",bc,backends[bc].comment or "no comment") + else + return nil + end +end) + +local comment = { "comment", "" } + +tables.vfspecials = allocate { + red = comment, + green = comment, + blue = comment, + black = comment, + startslant = comment, + stopslant = comment, +} + +-- can best be here + +interfaces.implement { + name = "setrealspaces", + arguments = "string", + actions = function(v) + setaction("shipouts","nodes.handlers.accessibility",v == variables.yes) + end +} + +-- moved to here + +local included = table.setmetatableindex( { + context = true, + id = true, + metadata = true, + date = true, + id = true, + pdf = true, +}, function(t,k) + return true +end) + +backends.included = included + +-- Also here: + +local paper_width = 0 +local paper_height = 0 + +function codeinjections.setpagedimensions(paperwidth,paperheight) + if paperwidth then + paper_width = paperwidth + end + if paperheight then + paper_height = paperheight + end + return paper_width, paper_height +end + +function codeinjections.getpagedimensions() + return paper_width, paper_height +end + +-- could also be codeinjections + +function backends.getcallbackstate() + return { count = status.late_callbacks or 0 } +end diff --git a/tex/context/base/mkxl/back-ini.mkxl b/tex/context/base/mkxl/back-ini.mkxl index 00bfeb188..1bf31413e 100644 --- a/tex/context/base/mkxl/back-ini.mkxl +++ b/tex/context/base/mkxl/back-ini.mkxl @@ -13,7 +13,7 @@ \writestatus{loading}{ConTeXt Backend Macros / Initialization} -\registerctxluafile{back-ini}{} +\registerctxluafile{back-ini}{autosuffix} \unprotect diff --git a/tex/context/base/mkxl/back-out.lmt b/tex/context/base/mkxl/back-out.lmt index 1f33da557..43fc04d78 100644 --- a/tex/context/base/mkxl/back-out.lmt +++ b/tex/context/base/mkxl/back-out.lmt @@ -61,8 +61,6 @@ local savenode = register(newnut(whatsit_code,whatsitcodes.save)) local restorenode = register(newnut(whatsit_code,whatsitcodes.restore)) local setmatrixnode = register(newnut(whatsit_code,whatsitcodes.setmatrix)) -local tomatrix = drivers.helpers.tomatrix - local open_command, write_command, close_command backends = backends or { } @@ -240,7 +238,7 @@ end function nodepool.setmatrix(rx,sx,sy,ry,tx,ty) local t = copynode(setmatrixnode) - nodeproperties[t] = { matrix = tomatrix(rx,sx,sy,ry,tx,ty) } + nodeproperties[t] = { rx, sx, sy, ry, tx, ty } return t end diff --git a/tex/context/base/mkxl/back-res.lmt b/tex/context/base/mkxl/back-res.lmt new file mode 100644 index 000000000..3157b37ef --- /dev/null +++ b/tex/context/base/mkxl/back-res.lmt @@ -0,0 +1,177 @@ +if not modules then modules = { } end modules ['back-res'] = { + version = 1.001, + comment = "companion to back-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local context = context + +local trace = false trackers.register("backend", function(v) trace = v end) +local report = logs.reporter("backend") + +local scanners = tokens.scanners +local scankeyword = scanners.keyword +local scaninteger = scanners.integer +local scanstring = scanners.string +local scandimension = scanners.dimension +local scanword = scanners.word +local scanwhd = scanners.whd + +local implement = interfaces.implement +local constants = interfaces.constants +local variables = interfaces.variables + +-- A box resource has an index. This happens to be an object number due to the pdf +-- backend but in fact it's an abstraction. This is why we have explicit fetchers. +-- The internal number (as in \Fm123) is yet another number. + +local tex_saveboxresource = tex.saveboxresource +local tex_useboxresource = tex.useboxresource +local tex_getboxresourcebox = tex.getboxresourcebox +local tex_getboxresourcedimensions = tex.getboxresourcedimensions + +updaters.register("backend.update",function() + tex_saveboxresource = tex.saveboxresource + tex_useboxresource = tex.useboxresource + tex_getboxresourcebox = tex.getboxresourcebox + tex_getboxresourcedimensions = tex.getboxresourcedimensions +end) + +local savebox = function(...) return tex_saveboxresource(...) end +local usebox = function(...) return tex_useboxresource(...) end +local getbox = function(...) return tex_getboxresourcebox(...) end +local getwhd = function(...) return tex_getboxresourcedimensions(...) end + +local boxresources = { + save = savebox, + use = usebox, + getbox = getbox, + getdimensions = getwhd, +} + +tex.boxresources = boxresources + +local lastindex = 0 + +local function saveboxresource() + local immediate = true + local kind = scankeyword("type") and scaninteger() or 0 + local attributes = scankeyword("attr") and scanstring() or nil + local resources = scankeyword("resources") and scanstring() or nil + local margin = scankeyword("margin") and scandimension() or 0 -- register + local boxnumber = scaninteger() + -- + lastindex = savebox(boxnumber,attributes,resources,immediate,kind,margin) + if trace then + report("\\saveboxresource: index %i",lastindex) + end +end + +local function lastsavedboxresourceindex() + if trace then + report("\\lastsaveboxresource: index %i",lastindex) + end + context("%i",lastindex) +end + +local function useboxresource() + local width, height, depth = scanwhd() + local index = scaninteger() + local node = usebox(index,width,height,depth) + if trace then + report("\\useboxresource: index %i",index) + end + context(node) +end + +implement { name = "saveboxresource", actions = saveboxresource } +implement { name = "lastsavedboxresourceindex", actions = lastsavedboxresourceindex } +implement { name = "useboxresource", actions = useboxresource } + +-- image resources + +local imageresources = { } +local lastindex = 0 +local lastpages = 1 + +local function saveimageresource() + local width, height, depth = scanwhd() + local page = 1 + local immediate = true + local margin = 0 -- or dimension + local attributes = scankeyword("attr") and scanstring() or nil + if scankeyword("named") then + scanstring() -- ignored + elseif scankeyword("page") then + page = scaninteger() + end + local userpassword = scankeyword("userpassword") and scanstring() or nil + local ownerpassword = scankeyword("ownerpassword") and scanstring() or nil + local visiblefilename = scankeyword("visiblefilename") and scanstring() or nil + local colorspace = scankeyword("colorspace") and scaninteger() or nil + local pagebox = scanword() or nil + local filename = scanstring() +-- pcall + context.getfiguredimensions( { filename }, { + [constants.userpassword] = userpassword, + [constants.ownerpassword] = ownerpassword, + [constants.page] = page or 1, + [constants.size] = pagebox, + }) + context.relax() + lastindex = lastindex + 1 + lastpages = 1 + imageresources[lastindex] = { + filename = filename, + page = page or 1, + size = pagebox, + width = width, + height = height, + depth = depth, + attr = attributes, + -- margin = margin, + } +end + +local function lastsavedimageresourceindex() + context("%i",lastindex or 0) +end + +local function lastsavedimageresourcepages() + context("%i",lastpages or 0) -- todo +end + +local function useimageresource() + local width, height, depth = scanwhd() + if scankeyword("keepopen") then + -- ignored + end + local index = scaninteger() + local l = imageresources[index] + if l then + if not (width or height or depth) then + width = l.width + height = l.height + depth = l.depth + end + -- pcall / we could use a dedicated call instead: + context.externalfigure( { l.filename }, { + [constants.userpassword] = l.userpassword, + [constants.ownerpassword] = l.ownerpassword, + [constants.width] = width and (width .. "sp") or nil, + [constants.height] = height and (height .. "sp") or nil, + [constants.page] = l.page or 1, + [constants.size] = pagebox, + }) + context.relax() + else + report("no valid image resource %a",index) + end +end + +implement { name = "saveimageresource", actions = saveimageresource } +implement { name = "lastsavedimageresourceindex", actions = lastsavedimageresourceindex } +implement { name = "lastsavedimageresourcepages", actions = lastsavedimageresourcepages } +implement { name = "useimageresource", actions = useimageresource } diff --git a/tex/context/base/mkxl/back-res.mkxl b/tex/context/base/mkxl/back-res.mkxl index 52317b946..8a18eef99 100644 --- a/tex/context/base/mkxl/back-res.mkxl +++ b/tex/context/base/mkxl/back-res.mkxl @@ -13,7 +13,7 @@ \writestatus{loading}{ConTeXt Backend Macros / Resources} -\registerctxluafile{back-res}{} +\registerctxluafile{back-res}{autosuffix} \unprotect diff --git a/tex/context/base/mkxl/back-trf.lmt b/tex/context/base/mkxl/back-trf.lmt new file mode 100644 index 000000000..1586bc440 --- /dev/null +++ b/tex/context/base/mkxl/back-trf.lmt @@ -0,0 +1,164 @@ +if not modules then modules = { } end modules ['back-trf'] = { + version = 1.001, + comment = "companion to back-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local sind, cosd, abs = math.sind, math.cosd, math.abs +local insert, remove = table.insert, table.remove +local unpack = unpack + +local context = context + +local formatters = string.formatters + +local scanners = tokens.scanners +local scankeyword = scanners.keyword +local scaninteger = scanners.integer +local scannumber = scanners.number +local scanstring = scanners.string + +local implement = interfaces.implement + +local nodepool = nodes.pool +local savenode = nodepool.save +local restorenode = nodepool.restore +local setmatrixnode = nodepool.setmatrix +local literalnode = nodepool.literal -- has to become some nodeinjection + +local stack = { } +local restore = true -- false + +updaters.register("backend.update",function() + savenode = nodepool.save + restorenode = nodepool.restore + setmatrixnode = nodepool.setmatrix + literalnode = nodepool.literal -- has to become some nodeinjection +end) + +local function stopsomething() + local top = remove(stack) + if top == false then + -- not wrapped + elseif top == true then + context(restorenode()) + elseif top then + context(setmatrixnode(unpack(top))) -- not really needed anymore + context(restorenode()) + else + -- nesting error + end +end + +local function startrotation() + local a = scannumber() + if a == 0 then + insert(stack,false) + else + local s, c = sind(a), cosd(a) + if abs(s) < 0.000001 then + s = 0 -- otherwise funny -0.00000 + end + if abs(c) < 0.000001 then + c = 0 -- otherwise funny -0.00000 + end + context(savenode()) + context(setmatrixnode(c,s,-s,c)) + insert(stack,restore and { c, -s, s, c } or true) + end +end + +implement { name = "startrotation", actions = startrotation } +implement { name = "stoprotation", actions = stopsomething } + +local function startscaling() -- at the tex end we use sx and sy instead of rx and ry + local rx, ry = 1, 1 + while true do + if scankeyword("rx") then + rx = scannumber() + elseif scankeyword("ry") then + ry = scannumber() + -- elseif scankeyword("revert") then + -- local top = stack[#stack] + -- if top then + -- rx = top[1] + -- ry = top[4] + -- else + -- rx = 1 + -- ry = 1 + -- end + else + break + end + end + if rx == 1 and ry == 1 then + insert(stack,false) + else + if rx == 0 then + rx = 0.0001 + end + if ry == 0 then + ry = 0.0001 + end + context(savenode()) + context(setmatrixnode(rx,0,0,ry)) + insert(stack,restore and { 1/rx, 0, 0, 1/ry } or true) + end +end + +implement { name = "startscaling", actions = startscaling } +implement { name = "stopscaling", actions = stopsomething } + +local function startmatrix() -- rx sx sy ry -- tx, ty + local rx, sx, sy, ry = 1, 0, 0, 1 + while true do + if scankeyword("rx") then rx = scannumber() + elseif scankeyword("ry") then ry = scannumber() + elseif scankeyword("sx") then sx = scannumber() + elseif scankeyword("sy") then sy = scannumber() + else break end + end + if rx == 1 and sx == 0 and sy == 0 and ry == 1 then + insert(stack,false) + else + context(savenode()) + context(setmatrixnode(rx,sx,sy,ry)) + insert(stack,store and { -rx, -sx, -sy, -ry } or true) + end +end + +implement { name = "startmatrix", actions = startmatrix } +implement { name = "stopmatrix", actions = stopsomething } + +local function startmirroring() + context(setmatrixnode(-1,0,0,1)) +end + +implement { name = "startmirroring", actions = startmirroring } +implement { name = "stopmirroring", actions = startmirroring } -- not: stopsomething + +-- this could also run on top of pack-rul ... todo + +-- local function startclipping() +-- -- context(savenode()) +-- context(literalnode("origin",formatters["q 0 w %s W n"](scanstring()))) +-- end +-- +-- local function stopclipping() +-- -- context(restorenode()) +-- context(literalnode("Q")) +-- end + +local function startclipping() + context(savenode()) + context(literalnode("origin",formatters["0 w %s W n"](scanstring()))) +end + +local function stopclipping() + context(restorenode()) +end + +implement { name = "startclipping", actions = startclipping } +implement { name = "stopclipping", actions = stopclipping } diff --git a/tex/context/base/mkxl/back-trf.mkxl b/tex/context/base/mkxl/back-trf.mkxl index 96479614d..fcf69ade4 100644 --- a/tex/context/base/mkxl/back-trf.mkxl +++ b/tex/context/base/mkxl/back-trf.mkxl @@ -13,7 +13,7 @@ \unprotect -\registerctxluafile{back-trf}{} +\registerctxluafile{back-trf}{autosuffix} % rotation diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index 9ba35aa2a..12ac9bafd 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.01 17:48} +\newcontextversion{2020.12.03 18:56} %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 e6b88efc5..1320962e7 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.01 17:48} +\immutable\edef\contextversion{2020.12.03 18:56} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/driv-ini.lmt b/tex/context/base/mkxl/driv-ini.lmt new file mode 100644 index 000000000..7b50a8cbf --- /dev/null +++ b/tex/context/base/mkxl/driv-ini.lmt @@ -0,0 +1,230 @@ +if not modules then modules = { } end modules ['driv-ini'] = { + version = 1.001, + comment = "companion to driv-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type = type +local addsuffix = file.addsuffix + +local setmetatableindex = table.setmetatableindex +local formatters = string.formatters + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +local report = logs.reporter("drivers") + +local instances = { } +local helpers = { } +local converters = { } +local prepared = { } +local wrappedup = { } +local cleanedup = { } +local currentdriver = "default" +local currentinstance = nil + +local shipout = tex.shipout +local texgetbox = tex.getbox +local texgetcount = tex.getcount + +function converters.engine(driver,boxnumber,mode,number,specification) + return shipout(boxnumber) +end + +local prepare = nil +local convert = nil +local wrapup = nil +local cleanup = nil +local outputfilename = nil + +drivers = drivers or { + instances = instances, + helpers = helpers, + converters = converters, + lmtxversion = 0.10, + report = report, +} + +local dummy = function() end + +local defaulthandlers = { + prepare = dummy, + initialize = dummy, + finalize = dummy, + updatefontstate = dummy, + wrapup = dummy, + cleanup = dummy, + convert = dummy, + outputfilename = dummy, +} + +function drivers.install(specification) + local name = specification.name + if not name then + report("missing driver name") + return + end + local actions = specification.actions + if not actions then + report("no actions for driver %a",name) + return + end + local flushers = specification.flushers + if not flushers then + report("no flushers for driver %a",name) + return + end + -- report("driver %a is installed",name) + setmetatableindex(actions, defaulthandlers) + setmetatableindex(flushers, function() return dummy end) + instances[name] = specification +end + +function drivers.convert(boxnumber) + if currentinstance then + callbacks.functions.start_page_number() + starttiming(drivers) + convert(currentinstance,boxnumber,texgetcount("realpageno")) + stoptiming(drivers) + callbacks.functions.stop_page_number() + end +end + +function drivers.outputfilename() + if currentinstance then + return outputfilename(currentinstance) + end +end + +luatex.wrapup(function() + if currentinstance and wrapup and not wrappedup[currentdriver] then + starttiming(drivers) + wrapup(currentinstance) + stoptiming(drivers) + wrappedup[currentdriver] = true + cleanedup[currentdriver] = true + end +end) + +luatex.cleanup(function() + if currentinstance and cleanup and not cleanedup[currentdriver] then + starttiming(drivers) + cleanup(currentinstance) + stoptiming(drivers) + wrappedup[currentdriver] = true + cleanedup[currentdriver] = true + end +end) + +function drivers.enable(name) + if name ~= currentdriver then + if currentinstance then + starttiming(drivers) + cleanup(currentinstance) + stoptiming(drivers) + end + currentdriver = name or "default" + currentinstance = instances[currentdriver] + if currentinstance then + local actions = currentinstance.actions + prepare = actions.prepare + wrapup = actions.wrapup + cleanup = actions.cleanup + convert = actions.convert + outputfilename = actions.outputfilename + -- + if prepare and not prepared[currentdriver] then + starttiming(drivers) + prepare(currentinstance) + stoptiming(drivers) + prepared[currentdriver] = true + end + else + report("bad driver") + end + end +end + +statistics.register("driver time",function() + return statistics.elapsedseconds(drivers) +end) + +interfaces.implement { + name = "shipoutpage", + arguments = "integer", + actions = drivers.convert, +} + +interfaces.implement { + name = "enabledriver", + arguments = "string", + actions = drivers.enable, +} + +-- The default driver: + +do + + local filename = nil + + drivers.install { + name = "default", + actions = { + convert = drivers.converters.engine, + outputfilename = function(driver) + if not filename then + filename = addsuffix(tex.jobname,"pdf") + end + return filename + end, + }, + flushers = { + -- we always need this entry + }, + } + +end + +-- No driver: + +do + + drivers.install { + name = "none", + actions = { }, + flushers = { }, + } + +end + +do + + local function prepare(driver) + converter = drivers.converters.lmtx + end + + local function convert(driver,boxnumber,pagenumber) + converter(driver,texgetbox(boxnumber),"page",pagenumber) + end + + drivers.install { + name = "empty", + actions = { + prepare = prepare, + convert = convert, + }, + flushers = { }, + } + +end + +-- + +setmetatableindex(instances,function() return instances.default end) + +-- for now: + +drivers.enable("default") diff --git a/tex/context/base/mkxl/driv-ini.mkxl b/tex/context/base/mkxl/driv-ini.mkxl index 9f489a2a1..947eb3777 100644 --- a/tex/context/base/mkxl/driv-ini.mkxl +++ b/tex/context/base/mkxl/driv-ini.mkxl @@ -13,7 +13,7 @@ \writestatus{loading}{ConTeXt Driver Macros / Initialization} -\registerctxluafile{driv-ini}{} +\registerctxluafile{driv-ini}{autosuffix} \unprotect diff --git a/tex/context/base/mkxl/font-lib.mklx b/tex/context/base/mkxl/font-lib.mklx index 780611148..08c5acbdf 100644 --- a/tex/context/base/mkxl/font-lib.mklx +++ b/tex/context/base/mkxl/font-lib.mklx @@ -74,7 +74,7 @@ \registerctxluafile{font-pat}{} % patchers -\registerctxluafile{node-fnt}{} % here +\registerctxluafile{node-fnt}{autosuffix} % here \registerctxluafile{font-mps}{} % outline fun diff --git a/tex/context/base/mkxl/lpdf-ini.lmt b/tex/context/base/mkxl/lpdf-ini.lmt index f7f45f5a3..97aeaa358 100644 --- a/tex/context/base/mkxl/lpdf-ini.lmt +++ b/tex/context/base/mkxl/lpdf-ini.lmt @@ -19,7 +19,6 @@ local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local formatters = string.formatters local isboolean = string.is_boolean local rshift = bit32.rshift -local osdate, ostime = os.date, os.time local report_objects = logs.reporter("backend","objects") local report_finalizing = logs.reporter("backend","finalizing") @@ -1073,8 +1072,10 @@ do -- It's a bit of a historical mess here. + local osdate, ostime, ostimezone = os.date, os.time, os.timezone + local metadata = nil - local timestamp = backends.timestamp() + local timestamp = osdate("%Y-%m-%dT%X") .. ostimezone(true) function lpdf.getmetadata() if not metadata then @@ -1100,7 +1101,7 @@ do n = converters.totime(n) if n then converters.settime(n) - timestamp = backends.timestamp() + timestamp = osdate("%Y-%m-%dT%X") .. ostimezone(true) -- probably not ok end end if metadata then diff --git a/tex/context/base/mkxl/lpdf-lmt.lmt b/tex/context/base/mkxl/lpdf-lmt.lmt index 32dfa574f..1cbe033a3 100644 --- a/tex/context/base/mkxl/lpdf-lmt.lmt +++ b/tex/context/base/mkxl/lpdf-lmt.lmt @@ -779,10 +779,6 @@ local flushsave, flushrestore, flushsetmatrix do local nofpositions = 0 local nofmatrices = 0 - local f_matrix = formatters["%s 0 0 cm"] - - local getdata = nuts.getdata - flushsave = function(current,pos_h,pos_v) nofpositions = nofpositions + 1 positions[nofpositions] = { pos_h, pos_v, nofmatrices } @@ -807,62 +803,79 @@ local flushsave, flushrestore, flushsetmatrix do nofpositions = nofpositions - 1 end - local function pdf_set_matrix(str,pos_h,pos_v) - if shippingmode == "page" then - local rx, sx, sy, ry = splitupstring(str," ") - if rx and ry and sx and ry then - rx, sx, sy, ry = tonumber(rx), tonumber(sx), tonumber(sy), tonumber(ry) - local tx = pos_h * (1 - rx) - pos_v * sy - local ty = pos_v * (1 - ry) - pos_h * sx - if nofmatrices > 0 then - local t = matrices[nofmatrices] - local r_x, s_x, s_y, r_y, te, tf = t[1], t[2], t[3], t[4], t[5], t[6] - rx, sx = rx * r_x + sx * s_y, rx * s_x + sx * r_y - sy, ry = sy * r_x + ry * s_y, sy * s_x + ry * r_y - tx, ty = tx * r_x + ty * s_y, tx * s_x + ty * r_y - end - nofmatrices = nofmatrices + 1 - matrices[nofmatrices] = { rx, sx, sy, ry, tx, ty } - end - end - end - local nodeproperties = nodes.properties.data + local s_matrix_0 = "1 0 0 1 0 0 cm" + local f_matrix_2 = formatters["%.6N 0 0 %.6N 0 0 cm"] + local f_matrix_4 = formatters["%.6N %.6N %.6N %.6N 0 0 cm"] + flushsetmatrix = function(current,pos_h,pos_v) - local str - if type(current) == "string" then - str = current - else - local p = nodeproperties[current] - if p then - str = p.matrix - else - str = getdata(current) -- for the moment + local p = nodeproperties[current] + if p then + local m = matrix + if m then + local rx = m.rx + local sx = m.sx + local sy = m.sy + local ry = m.ry + if not rx then + rx = 1 + elseif rx == 0 then + rx = 0.0001 + end + if not ry then + ry = 1 + elseif ry == 0 then + ry = 0.0001 + end + if not sx then + sx = 0 + end + if not sy then + sy = 0 + end + -- + if shippingmode == "page" then + local tx = pos_h * (1 - rx) - pos_v * sy + local ty = pos_v * (1 - ry) - pos_h * sx + if nofmatrices > 0 then + local t = matrices[nofmatrices] + local r_x, s_x, s_y, r_y, te, tf = t[1], t[2], t[3], t[4], t[5], t[6] + rx, sx = rx * r_x + sx * s_y, rx * s_x + sx * r_y + sy, ry = sy * r_x + ry * s_y, sy * s_x + ry * r_y + tx, ty = tx * r_x + ty * s_y, tx * s_x + ty * r_y + end + nofmatrices = nofmatrices + 1 + matrices[nofmatrices] = { rx, sx, sy, ry, tx, ty } + end + -- + pdf_goto_pagemode() + 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 end end - if str and str ~= "" then - pdf_set_matrix(str,pos_h,pos_v) - pdf_goto_pagemode() - pdf_set_pos(pos_h,pos_v) - b = b + 1 ; buffer[b] = f_matrix(str) - end end - do - - function lpdf.hasmatrix() - return nofmatrices > 0 - end + function lpdf.hasmatrix() + return nofmatrices > 0 + end - function lpdf.getmatrix() - if nofmatrices > 0 then - return unpack(matrices[nofmatrices]) - else - return 1, 0, 0, 1, 0, 0 - end + function lpdf.getmatrix() + if nofmatrices > 0 then + return unpack(matrices[nofmatrices]) + else + return 1, 0, 0, 1, 0, 0 end - end pushorientation = function(orientation,pos_h,pos_v,pos_r) @@ -884,21 +897,6 @@ local flushsave, flushrestore, flushsetmatrix do b = b + 1 ; buffer[b] = "Q" end - -- pushorientation = function(orientation,pos_h,pos_v,pos_r) - -- flushsave(false,pos_h,pos_v) - -- if orientation == 1 then - -- flushsetmatrix("0 -1 1 0",pos_h,pos_v) - -- elseif orientation == 2 then - -- flushsetmatrix("-1 0 0 -1",pos_h,pos_v) - -- elseif orientation == 3 then - -- flushsetmatrix("0 1 -1 0",pos_h,pos_v) - -- end - -- end - - -- poporientation = function(orientation,pos_h,pos_v,pos_r) - -- flushrestore(false,pos_h,pos_v) - -- end - end -- rules @@ -1682,19 +1680,13 @@ local finalize do pageresources.XObject = xforms pageresources.ProcSet = lpdf.procset() - local xorigin, yorigin, relocated = backends.codeinjections.getpageorigin() -- for now here - local bbox = pdfarray { - (boundingbox[1] + xorigin) * bpfactor, - (boundingbox[2] + yorigin) * bpfactor, - (boundingbox[3] + xorigin) * bpfactor, - (boundingbox[4] + yorigin) * bpfactor, + boundingbox[1] * bpfactor, + boundingbox[2] * bpfactor, + boundingbox[3] * bpfactor, + boundingbox[4] * bpfactor, } - if relocated then - content = formatters["1 0 0 1 %.6N %.6N cm\n%s"](bbox[1],bbox[2],content) - end - local contentsobj = pdfflushstreamobject(content,false,false) pageattributes.Type = pdf_page @@ -1716,12 +1708,6 @@ local finalize do local CropBox = pageattributes.CropBox local BleedBox = pageattributes.BleedBox - if relocated then - if TrimBox then TrimBox = box end - if CropBox then CropBox = box end - if BleedBox then BleedBox = box end - end - -- Indirect objects don't work in all viewers. if TrimBox then pageattributes.TrimBox = pdfsharedobject(TrimBox ) end @@ -2916,9 +2902,6 @@ do lpdf.registerdocumentfinalizer(wrapup,nil,"wrapping up") -- end - -- - environment.lmtxmode = true -- CONTEXTLMTXMODE - -- converter = drivers.converters.lmtx useddriver = driver end diff --git a/tex/context/base/mkxl/lpdf-mis.lmt b/tex/context/base/mkxl/lpdf-mis.lmt index 6870a1ad4..46accf9a4 100644 --- a/tex/context/base/mkxl/lpdf-mis.lmt +++ b/tex/context/base/mkxl/lpdf-mis.lmt @@ -251,12 +251,6 @@ local function setupidentity() addtoinfo("ID", pdfstring(id), id) -- needed for pdf/x -- addtoinfo("ConTeXt.Version",version) - -- - local lmtx = codeinjections.lmtxmode() - if lmtx then - addtoinfo("ConTeXt.LMTX",formatters["%0.2f"](lmtx)) - end - -- addtoinfo("ConTeXt.Time",os.date("%Y-%m-%d %H:%M")) addtoinfo("ConTeXt.Jobname",jobname) addtoinfo("ConTeXt.Url","www.pragma-ade.com") diff --git a/tex/context/base/mkxl/meta-fig.mkxl b/tex/context/base/mkxl/meta-fig.mkxl index 9ddd5e394..4999bc4b2 100644 --- a/tex/context/base/mkxl/meta-fig.mkxl +++ b/tex/context/base/mkxl/meta-fig.mkxl @@ -48,9 +48,7 @@ {\setupfittingpage[MPpage]} \protected\def\meta_process_graphic_command - {\doif{\fittingpageparameter\c!alternative}\v!offset - {\def\meta_relocate_graphic{\clf_setpageorigin\MPllx\MPlly\relax}}% - \meta_process_graphic_instance{\fittingpageparameter\c!instance}} + {\meta_process_graphic_instance{\fittingpageparameter\c!instance}} %D \macros %D {MPfigure} diff --git a/tex/context/base/mkxl/node-fin.mkxl b/tex/context/base/mkxl/node-fin.mkxl index b2217d3ca..fa2a01f82 100644 --- a/tex/context/base/mkxl/node-fin.mkxl +++ b/tex/context/base/mkxl/node-fin.mkxl @@ -15,9 +15,8 @@ \unprotect -\registerctxluafile{node-shp}{optimize} -\registerctxluafile{node-fin}{autosuffix,optimize} % we might generalize this one -% \registerctxluafile{node-fin}{optimize} % we might generalize this one +\registerctxluafile{node-shp}{autosuffix,optimize} +\registerctxluafile{node-fin}{autosuffix,optimize} \permanent\protected\def\finalizeobjectbox #1{\clf_finalizebox#1\relax} \permanent\protected\def\finalizeshipoutbox#1{\clf_finalizebox#1\relax} diff --git a/tex/context/base/mkxl/node-fnt.lmt b/tex/context/base/mkxl/node-fnt.lmt new file mode 100644 index 000000000..658ea46bb --- /dev/null +++ b/tex/context/base/mkxl/node-fnt.lmt @@ -0,0 +1,580 @@ +if not modules then modules = { } end modules ['node-fnt'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +if not context then os.exit() end -- generic function in node-dum + +local next, type = next, type +local concat, keys = table.concat, table.keys + +local nodes, node, fonts = nodes, node, fonts + +local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end) +local trace_fontrun = false trackers.register("nodes.fontrun", function(v) trace_fontrun = v end) +local trace_variants = false trackers.register("nodes.variants", function(v) trace_variants = v end) + +-- bad namespace for directives + +local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end) +local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end) +local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end) +local keep_redundant = false directives.register("nodes.keepredundant",function(v) keep_redundant = v end) + +local report_fonts = logs.reporter("fonts","processing") + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontvariants = fonthashes.variants +local fontmodes = fonthashes.modes + +local otf = fonts.handlers.otf + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +local nodecodes = nodes.nodecodes +local boundarycodes = nodes.boundarycodes + +local handlers = nodes.handlers + +local nuts = nodes.nuts + +local getid = nuts.getid +local getsubtype = nuts.getsubtype +local getreplace = nuts.getreplace +local getnext = nuts.getnext +local getprev = nuts.getprev +local getboth = nuts.getboth +local getdata = nuts.getdata +local getglyphdata = nuts.getglyphdata + +local setchar = nuts.setchar +local setlink = nuts.setlink +local setnext = nuts.setnext +local setprev = nuts.setprev + +local isglyph = nuts.isglyph -- unchecked +local ischar = nuts.ischar -- checked + +----- traverse_id = nuts.traverse_id +----- traverse_char = nuts.traverse_char +local nextboundary = nuts.traversers.boundary +local nextdisc = nuts.traversers.disc +local nextchar = nuts.traversers.char + +local flush_node = nuts.flush + +local disc_code = nodecodes.disc +local boundary_code = nodecodes.boundary + +local wordboundary_code = boundarycodes.word + +local protect_glyphs = nuts.protect_glyphs +local unprotect_glyphs = nuts.unprotect_glyphs + +local setmetatableindex = table.setmetatableindex + +-- some tests with using an array of dynamics[id] and processes[id] demonstrated +-- that there was nothing to gain (unless we also optimize other parts) +-- +-- maybe getting rid of the intermediate shared can save some time + +local run = 0 + +local setfontdynamics = { } +local fontprocesses = { } + +-- setmetatableindex(setfontdynamics, function(t,font) +-- local tfmdata = fontdata[font] +-- local shared = tfmdata.shared +-- local v = shared and shared.dynamics and otf.setdynamics or false +-- t[font] = v +-- return v +-- end) + +setmetatableindex(setfontdynamics, function(t,font) + local tfmdata = fontdata[font] + local shared = tfmdata.shared + local f = shared and shared.dynamics and otf.setdynamics or false + if f then + local v = { } + t[font] = v + setmetatableindex(v,function(t,k) + local v = f(font,k) + t[k] = v + return v + end) + return v + else + t[font] = false + return false + end +end) + +setmetatableindex(fontprocesses, function(t,font) + local tfmdata = fontdata[font] + local shared = tfmdata.shared -- we need to check shared, only when same features + local processes = shared and shared.processes + if processes and #processes > 0 then + t[font] = processes + return processes + else + t[font] = false + return false + end +end) + +fonts.hashes.setdynamics = setfontdynamics +fonts.hashes.processes = fontprocesses + +-- if we forget about basemode we don't need to test too much here and we can consider running +-- over sub-ranges .. this involves a bit more initializations but who cares .. in that case we +-- also need to use the stop criterium (we already use head too) ... we cannot use traverse +-- then, so i'll test it on some local clone first ... the only pitfall is changed directions +-- inside a run which means that we need to keep track of this which in turn complicates matters +-- in a way i don't like + +-- we need to deal with the basemode fonts here and can only run over ranges as we otherwise get +-- luatex craches due to all kind of asserts in the disc/lig builder + +-- there is no gain in merging used (dynamic 0) and dynamics apart from a bit less code + +local ligaturing = nuts.ligaturing +local kerning = nuts.kerning + +local function start_trace(head) + run = run + 1 + report_fonts() + report_fonts("checking node list, run %s",run) + report_fonts() + local n = head + while n do + local char, id = isglyph(n) + if char then + local font = id + local attr = getglyphdata(n) or 0 + report_fonts("font %03i, dynamic %03i, glyph %C",font,attr,char) + elseif id == disc_code then + report_fonts("[disc] %s",nodes.listtoutf(n,true,false,n)) + elseif id == boundary_code then + report_fonts("[boundary] %i:%i",getsubtype(n),getdata(n)) + else + report_fonts("[%s]",nodecodes[id]) + end + n = getnext(n) + end +end + +local function stop_trace(u,usedfonts,a,attrfonts,b,basefonts,r,redundant) + report_fonts() + report_fonts("statics : %s",u > 0 and concat(keys(usedfonts)," ") or "none") + report_fonts("dynamics: %s",a > 0 and concat(keys(attrfonts)," ") or "none") + report_fonts("built-in: %s",b > 0 and b or "none") + report_fonts("removed : %s",r > 0 and r or "none") + report_fonts() +end + +do + + local usedfonts + local attrfonts + local basefonts -- could be reused + local basefont + local prevfont + local prevattr + local variants + local redundant -- could be reused + local firstnone + local lastfont + local lastproc + local lastnone + + local a, u, b, r + + local function protectnone() + protect_glyphs(firstnone,lastnone) + firstnone = nil + end + + local function setnone(n) + if firstnone then + protectnone() + end + if basefont then + basefont[2] = getprev(n) + basefont = false + end + if not firstnone then + firstnone = n + end + lastnone = n + end + + local function setbase(n) + if firstnone then + protectnone() + end + if force_basepass then + if basefont then + basefont[2] = getprev(n) + end + b = b + 1 + basefont = { n, false } + basefonts[b] = basefont + end + end + + local function setnode(n,font,attr) -- we could use prevfont and prevattr when we set then first + if firstnone then + protectnone() + end + if basefont then + basefont[2] = getprev(n) + basefont = false + end + if attr > 0 then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + local fd = setfontdynamics[font] + if fd then + used[attr] = fd[attr] + a = a + 1 + end + end + else + local used = usedfonts[font] + if not used then + lastfont = font + lastproc = fontprocesses[font] + if lastproc then + usedfonts[font] = lastproc + u = u + 1 + end + end + end + end + + function handlers.characters(head,groupcode,size,packtype,direction) + -- either next or not, but definitely no already processed list + starttiming(nodes) + + usedfonts = { } + attrfonts = { } + basefonts = { } + basefont = nil + prevfont = nil + prevattr = 0 + variants = nil + redundant = nil + firstnone = nil + lastfont = nil + lastproc = nil + lastnone = nil + +local fontmode = nil + + a, u, b, r = 0, 0, 0, 0 + + if trace_fontrun then + start_trace(head) + end + + -- There is no gain in checking for a single glyph and then having a fast path. On the + -- metafun manual (with some 2500 single char lists) the difference is just noise. + + for n, char, font in nextchar, head do + +-- local attr = getglyphdata(n) or 0 -- zero attribute is reserved for fonts in context +-- if font ~= prevfont or attr ~= prevattr then +-- prevfont = font +-- prevattr = attr +-- variants = fontvariants[font] +-- local fontmode = fontmodes[font] +-- if fontmode == "none" then +-- setnone(n) +-- elseif fontmode == "base" then +-- setbase(n) +-- else +-- setnode(n,font,attr) +-- end +-- elseif firstnone then +-- lastnone = n +-- end + + if font ~= prevfont then + prevfont = font + fontmode = fontmodes[font] + if fontmode == "none" then + prevattr = 0 + variants = false + setnone(n) + elseif fontmode == "base" then + prevattr = 0 + variants = false + setbase(n) + else + local attr = getglyphdata(n) or 0 -- zero attribute is reserved for fonts in context + prevattr = attr + variants = fontvariants[font] + setnode(n,font,attr) + end + elseif fontmode == "node" then + local attr = getglyphdata(n) or 0 -- zero attribute is reserved for fonts in context + if attr ~= prevattr then + prevattr = attr + variants = fontvariants[font] + setnode(n,font,attr) + end + elseif firstnone then + lastnone = n + end + + if variants then + if (char >= 0xFE00 and char <= 0xFE0F) or (char >= 0xE0100 and char <= 0xE01EF) then + -- if variants and char >= 0xFE00 then + -- if char < 0xFE0F or (char >= 0xE0100 and char <= 0xE01EF) then + local hash = variants[char] + if hash then + local p = getprev(n) + if p then + local char = ischar(p) -- checked + local variant = hash[char] + if variant then + if trace_variants then + report_fonts("replacing %C by %C",char,variant) + end + setchar(p,variant) + if redundant then + r = r + 1 + redundant[r] = n + else + r = 1 + redundant = { n } + end + end + end + elseif keep_redundant then + -- go on, can be used for tracing + elseif redundant then + r = r + 1 + redundant[r] = n + else + r = 1 + redundant = { n } + end + end + end + + end + + if firstnone then + protectnone() + end + + if force_boundaryrun then + + -- we can inject wordboundaries and then let the hyphenator do its work + -- but we need to get rid of those nodes in order to build ligatures + -- and kern (a rather context thing) + + for b, subtype in nextboundary, head do + if subtype == wordboundary_code then + if redundant then + r = r + 1 + redundant[r] = b + else + r = 1 + redundant = { b } + end + end + end + + end + + if redundant then + for i=1,r do + local r = redundant[i] + local p, n = getboth(r) + if r == head then + head = n + setprev(n) + else + setlink(p,n) + end + if b > 0 then + for i=1,b do + local bi = basefonts[i] + local b1 = bi[1] + local b2 = bi[2] + if b1 == b2 then + if b1 == r then + bi[1] = false + bi[2] = false + end + elseif b1 == r then + bi[1] = n + elseif b2 == r then + bi[2] = p + end + end + end + flush_node(r) + end + end + + if force_discrun then + -- basefont is not supported in disc only runs ... it would mean a lot of + -- ranges .. we could try to run basemode as a separate processor run but not + -- for now (we can consider it when the new node code is tested + for d in nextdisc, head do + -- doing only replace is good enough because pre and post are normally used + -- for hyphens and these come from fonts that part of the hyphenated word + local r = getreplace(d) + if r then + local prevfont = nil + local prevattr = nil + local none = false + firstnone = nil + basefont = nil + for n, char, font in nextchar, r do + local attr = getglyphdata(n) or 0 -- zero attribute is reserved for fonts in context + if font ~= prevfont or attr ~= prevattr then + prevfont = font + prevattr = attr + local fontmode = fontmodes[font] + if fontmode == "none" then + setnone(n) + elseif fontmode == "base" then + -- so the replace gets an extra treatment ... so be it + setbase(n) + else + setnode(n,font,attr) + end + elseif firstnone then + -- lastnone = n + lastnone = nil + end + -- we assume one font for now (and if there are more and we get into issues then + -- we can always remove the break) + break + end + if firstnone then + protectnone() + end + end + end + + end + + if trace_fontrun then + stop_trace(u,usedfonts,a,attrfonts,b,basefonts,r,redundant) + end + + -- in context we always have at least 2 processors + if u == 0 then + -- skip + elseif u == 1 then + local attr = a > 0 and 0 or false -- 0 is the savest way + for i=1,#lastproc do + head = lastproc[i](head,lastfont,attr,direction) + end + else + -- local attr = a == 0 and false or 0 -- 0 is the savest way + local attr = a > 0 and 0 or false -- 0 is the savest way + for font, processors in next, usedfonts do -- unordered + for i=1,#processors do + head = processors[i](head,font,attr,direction,u) + end + end + end + if a == 0 then + -- skip + elseif a == 1 then + local font, dynamics = next(attrfonts) + for attribute, processors in next, dynamics do -- unordered, attr can switch in between + for i=1,#processors do + head = processors[i](head,font,attribute,direction) + end + end + else + for font, dynamics in next, attrfonts do + for attribute, processors in next, dynamics do -- unordered, attr can switch in between + for i=1,#processors do + head = processors[i](head,font,attribute,direction,a) + end + end + end + end + if b == 0 then + -- skip + elseif b == 1 then + -- only one font + local range = basefonts[1] + local start = range[1] + local stop = range[2] + if (start or stop) and (start ~= stop) then + local front = head == start + if stop then + start = ligaturing(start,stop) + start = kerning(start,stop) + elseif start then -- safeguard + start = ligaturing(start) + start = kerning(start) + end + if front and head ~= start then + head = start + end + end + else + -- multiple fonts + for i=1,b do + local range = basefonts[i] + local start = range[1] + local stop = range[2] + if start then -- and start ~= stop but that seldom happens + local front = head == start + local prev = getprev(start) + local next = getnext(stop) + if stop then + start, stop = ligaturing(start,stop) + start, stop = kerning(start,stop) + else + start = ligaturing(start) + start = kerning(start) + end + -- is done automatically + if prev then + setlink(prev,start) + end + if next then + setlink(stop,next) + end + -- till here + if front and head ~= start then + head = start + end + end + end + end + + stoptiming(nodes) + + if trace_characters then + nodes.report(head) + end + + return head + end + +end + +handlers.protectglyphs = protect_glyphs +handlers.unprotectglyphs = unprotect_glyphs diff --git a/tex/context/base/mkxl/node-ini.mkxl b/tex/context/base/mkxl/node-ini.mkxl index 4cb322448..a7343469e 100644 --- a/tex/context/base/mkxl/node-ini.mkxl +++ b/tex/context/base/mkxl/node-ini.mkxl @@ -21,7 +21,7 @@ \registerctxluafile{node-ini}{autosuffix} \registerctxluafile{node-met}{} \registerctxluafile{node-nut}{autosuffix} -\registerctxluafile{node-res}{} +\registerctxluafile{node-res}{autosuffix} %registerctxluafile{node-ppt}{} % experimental, not used so probably useless \registerctxluafile{node-aux}{autosuffix} \registerctxluafile{node-gcm}{autosuffix} @@ -36,7 +36,7 @@ \registerctxluafile{node-ext}{} \registerctxluafile{node-acc}{} % experimental %registerctxluafile{node-prp}{} % makes no sense (yet) -\registerctxluafile{node-scn}{} +\registerctxluafile{node-scn}{autosuffix} \registerctxluafile{node-syn}{} \registerctxluafile{node-par}{autosuffix} diff --git a/tex/context/base/mkxl/node-res.lmt b/tex/context/base/mkxl/node-res.lmt new file mode 100644 index 000000000..c57e5cfd0 --- /dev/null +++ b/tex/context/base/mkxl/node-res.lmt @@ -0,0 +1,622 @@ +if not modules then modules = { } end modules ['node-res'] = { + 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 type, next = type, next +local gmatch, format = string.gmatch, string.format + +--[[ldx-- +<p>The next function is not that much needed but in <l n='context'/> we use +for debugging <l n='luatex'/> node management.</p> +--ldx]]-- + +local nodes, node = nodes, node + +local report_nodes = logs.reporter("nodes","housekeeping") + +nodes.pool = nodes.pool or { } +local nodepool = nodes.pool + +local whatsitcodes = nodes.whatsitcodes +local gluecodes = nodes.gluecodes +local kerncodes = nodes.kerncodes +local rulecodes = nodes.rulecodes +local nodecodes = nodes.nodecodes +local boundarycodes = nodes.boundarycodes +local usercodes = nodes.usercodes + +local nodeproperties = nodes.properties.data + +local glyph_code = nodecodes.glyph +local rule_code = nodecodes.rule +local kern_code = nodecodes.kern +local glue_code = nodecodes.glue +local gluespec_code = nodecodes.gluespec +local whatsit_code = nodecodes.whatsit + +local currentfont = font.current +local texgetcount = tex.getcount + +local allocate = utilities.storage.allocate + +local reserved = { } +local nofreserved = 0 +local userids = allocate() +local lastid = 0 + +setmetatable(userids, { + __index = function(t,k) + if type(k) == "string" then + lastid = lastid + 1 + rawset(userids,lastid,k) + rawset(userids,k,lastid) + return lastid + else + rawset(userids,k,k) + return k + end + end, + __call = function(t,k) + return t[k] + end +} ) + +-- nuts overload + +local nuts = nodes.nuts +local nutpool = { } +nuts.pool = nutpool + +local tonut = nuts.tonut +local tonode = nuts.tonode + +local getbox = nuts.getbox +local getid = nuts.getid +local getlist = nuts.getlist +local getglue = nuts.getglue + +local setfield = nuts.setfield +local setchar = nuts.setchar +local setlist = nuts.setlist +local setwhd = nuts.setwhd +local setglue = nuts.setglue +local setdisc = nuts.setdisc +local setfont = nuts.setfont +local setkern = nuts.setkern +local setpenalty = nuts.setpenalty +local setdir = nuts.setdir +local setdirection = nuts.setdirection +local setshift = nuts.setshift +local setwidth = nuts.setwidth +local setsubtype = nuts.setsubtype +local setleader = nuts.setleader + +local setdata = nuts.setdata +local setruledata = nuts.setruledata +local setvalue = nuts.setvalue + +local copy_nut = nuts.copy_only or nuts.copy +local new_nut = nuts.new +local flush_nut = nuts.flush + +-- at some point we could have a dual set (the overhead of tonut is not much larger than +-- metatable associations at the lua/c end esp if we also take assignments into account + +-- table.setmetatableindex(nodepool,function(t,k,v) +-- -- report_nodes("defining nodepool[%s] instance",k) +-- local f = nutpool[k] +-- local v = function(...) +-- return tonode(f(...)) +-- end +-- t[k] = v +-- return v +-- end) +-- +-- -- we delay one step because that permits us a forward reference +-- -- e.g. in pdfsetmatrix + +table.setmetatableindex(nodepool,function(t,k,v) + -- report_nodes("defining nodepool[%s] instance",k) + local v = function(...) + local f = nutpool[k] + local v = function(...) + return tonode(f(...)) + end + t[k] = v + return v(...) + end + t[k] = v + return v +end) + +local function register_nut(n) + nofreserved = nofreserved + 1 + reserved[nofreserved] = n + return n +end + +local function register_node(n) + nofreserved = nofreserved + 1 + if type(n) == "number" then -- isnut(n) + reserved[nofreserved] = n + else + reserved[nofreserved] = tonut(n) + end + return n +end + +nodepool.userids = userids +nodepool.register = register_node + +nutpool.userids = userids +nutpool.register = register_node -- could be register_nut + +-- so far + +local disc = register_nut(new_nut(nodecodes.disc)) +local kern = register_nut(new_nut(kern_code,kerncodes.userkern)) +local fontkern = register_nut(new_nut(kern_code,kerncodes.fontkern)) +local italickern = register_nut(new_nut(kern_code,kerncodes.italiccorrection)) +local penalty = register_nut(new_nut(nodecodes.penalty)) +local glue = register_nut(new_nut(glue_code)) +local gluespec = register_nut(new_nut(gluespec_code)) +local glyph = register_nut(new_nut(glyph_code,0)) + +local textdir = register_nut(new_nut(nodecodes.dir)) + +local latelua = register_nut(new_nut(whatsit_code,whatsitcodes.latelua)) +local savepos = register_nut(new_nut(whatsit_code,whatsitcodes.savepos)) + +local user_node = new_nut(whatsit_code,whatsitcodes.userdefined) + +local left_margin_kern = register_nut(new_nut(kern_code,kerncodes.leftmargincode)) +local right_margin_kern = register_nut(new_nut(kern_code,kerncodes.rightmargincode)) + +local lineskip = register_nut(new_nut(glue_code,gluecodes.lineskip)) +local baselineskip = register_nut(new_nut(glue_code,gluecodes.baselineskip)) +local leftskip = register_nut(new_nut(glue_code,gluecodes.leftskip)) +local rightskip = register_nut(new_nut(glue_code,gluecodes.rightskip)) +local lefthangskip = register_nut(new_nut(glue_code,gluecodes.lefthangskip)) +local righthangskip = register_nut(new_nut(glue_code,gluecodes.righthangskip)) +local indentskip = register_nut(new_nut(glue_code,gluecodes.indentskip)) +local correctionskip = register_nut(new_nut(glue_code,gluecodes.correctionskip)) + +local temp = register_nut(new_nut(nodecodes.temp,0)) + +local noad = register_nut(new_nut(nodecodes.noad)) +local delimiter = register_nut(new_nut(nodecodes.delimiter)) +local fence = register_nut(new_nut(nodecodes.fence)) +local submlist = register_nut(new_nut(nodecodes.submlist)) +local accent = register_nut(new_nut(nodecodes.accent)) +local radical = register_nut(new_nut(nodecodes.radical)) +local fraction = register_nut(new_nut(nodecodes.fraction)) +local subbox = register_nut(new_nut(nodecodes.subbox)) +local mathchar = register_nut(new_nut(nodecodes.mathchar)) +local mathtextchar = register_nut(new_nut(nodecodes.mathtextchar)) +local choice = register_nut(new_nut(nodecodes.choice)) + +local boundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.user)) +local wordboundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.word)) + +local cleader = register_nut(copy_nut(glue)) setsubtype(cleader,gluecodes.cleaders) setglue(cleader,0,65536,0,2,0) + +-- the dir field needs to be set otherwise crash: + +local lefttoright_code = nodes.dirvalues.lefttoright + +local rule = register_nut(new_nut(rule_code)) -- setdirection(rule, lefttoright_code) +local emptyrule = register_nut(new_nut(rule_code,rulecodes.empty)) -- setdirection(rule, lefttoright_code) +local userrule = register_nut(new_nut(rule_code,rulecodes.user)) -- setdirection(rule, lefttoright_code) +local outlinerule = register_nut(new_nut(rule_code,rulecodes.outline)) -- setdirection(rule, lefttoright_code) +local hlist = register_nut(new_nut(nodecodes.hlist)) setdirection(hlist,lefttoright_code) +local vlist = register_nut(new_nut(nodecodes.vlist)) setdirection(vlist,lefttoright_code) + +function nutpool.glyph(fnt,chr) + local n = copy_nut(glyph) + if fnt then + setfont(n,fnt == true and currentfont() or fnt,chr) + elseif chr then + setchar(n,chr) + end + return n +end + +function nutpool.penalty(p) + local n = copy_nut(penalty) + if p and p ~= 0 then + setpenalty(n,p) + end + return n +end + +function nutpool.kern(k) + local n = copy_nut(kern) + if k and k ~= 0 then + setkern(n,k) + end + return n +end + +function nutpool.boundary(v) + local n = copy_nut(boundary) + if v and v ~= 0 then + setvalue(n,v) + end + return n +end + +function nutpool.wordboundary(v) + local n = copy_nut(wordboundary) + if v and v ~= 0 then + setvalue(n,v) + end + return n +end + +function nutpool.fontkern(k) + local n = copy_nut(fontkern) + if k and k ~= 0 then + setkern(n,k) + end + return n +end + +function nutpool.italickern(k) + local n = copy_nut(italickern) + if k and k ~= 0 then + setkern(n,k) + end + return n +end + +function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order) + local n = copy_nut(gluespec) + if width or stretch or shrink or stretch_order or shrink_order then + setglue(n,width,stretch,shrink,stretch_order,shrink_order) + end + return n +end + +local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order) + -- maybe setglue + local n = copy_nut(skip) + if width or stretch or shrink or stretch_order or shrink_order then + setglue(n,width,stretch,shrink,stretch_order,shrink_order) + end + return n +end + +function nutpool.stretch(a,b) + -- width stretch shrink stretch_order shrink_order + local n = copy_nut(glue) + if not b then + a, b = 1, a or 1 + end + setglue(n,0,a,0,b,0) + return n +end + +function nutpool.shrink(a,b) + local n = copy_nut(glue) + if not b then + a, b = 1, a or 1 + end + setglue(n,0,0,a,0,0,b) + return n +end + +function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order) + return someskip(glue,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.negatedglue(glue) + local n = copy_nut(glue) + local width, stretch, shrink = getglue(n) + setglue(n,-width,-stretch,-shrink) + return n +end + +function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.lefthangskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(lefthangskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.righthangskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(righthangskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.indentskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(indentskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order) + return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order) +end + +function nutpool.baselineskip(width,stretch,shrink) + return someskip(baselineskip,width,stretch,shrink) +end + +function nutpool.disc(pre,post,replace) + local d = copy_nut(disc) + if pre or post or replace then + setdisc(d,pre,post,replace) + end + return d +end + +function nutpool.direction(dir,swap) + local t = copy_nut(textdir) + if not dir then + -- just a l2r start node + elseif swap then + setdirection(t,dir,true) + else + setdirection(t,dir,false) + end + return t +end + +function nutpool.rule(width,height,depth) -- w/h/d == nil will let them adapt + local n = copy_nut(rule) + if width or height or depth then + setwhd(n,width,height,depth) + end + return n +end + +function nutpool.emptyrule(width,height,depth) -- w/h/d == nil will let them adapt + local n = copy_nut(emptyrule) + if width or height or depth then + setwhd(n,width,height,depth) + end + return n +end + +function nutpool.userrule(width,height,depth) -- w/h/d == nil will let them adapt + local n = copy_nut(userrule) + if width or height or depth then + setwhd(n,width,height,depth) + end + return n +end + +function nutpool.outlinerule(width,height,depth,line) -- w/h/d == nil will let them adapt + local n = copy_nut(outlinerule) + if width or height or depth then + setwhd(n,width,height,depth) + end + if line then + setruledata(n,line) + end + return n +end + +function nutpool.leader(width,list) + local n = copy_nut(cleader) + if width then + setwidth(n,width) + end + if list then + setleader(n,list) + end + return n +end + +function nutpool.savepos() + return copy_nut(savepos) +end + +function nutpool.latelua(code) + local n = copy_nut(latelua) + nodeproperties[n] = { data = code } + return n +end + +function nutpool.leftmarginkern(glyph,width) + local n = copy_nut(left_margin_kern) + if not glyph then + report_nodes("invalid pointer to left margin glyph node") + elseif getid(glyph) ~= glyph_code then + report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left") + else + setfield(n,"glyph",glyph) + end + if width and width ~= 0 then + setwidth(n,width) + end + return n +end + +function nutpool.rightmarginkern(glyph,width) + local n = copy_nut(right_margin_kern) + if not glyph then + report_nodes("invalid pointer to right margin glyph node") + elseif getid(glyph) ~= glyph_code then + report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right") + else + setfield(n,"glyph",glyph) + end + if width and width ~= 0 then + setwidth(n,width) + end + return n +end + +function nutpool.temp() + return copy_nut(temp) +end + +function nutpool.noad() return copy_nut(noad) end +function nutpool.delimiter() return copy_nut(delimiter) end nutpool.delim = nutpool.delimiter +function nutpool.fence() return copy_nut(fence) end +function nutpool.submlist() return copy_nut(submlist) end +function nutpool.noad() return copy_nut(noad) end +function nutpool.fence() return copy_nut(fence) end +function nutpool.accent() return copy_nut(accent) end +function nutpool.radical() return copy_nut(radical) end +function nutpool.fraction() return copy_nut(fraction) end +function nutpool.subbox() return copy_nut(subbox) end +function nutpool.mathchar() return copy_nut(mathchar) end +function nutpool.mathtextchar() return copy_nut(mathtextchar) end +function nutpool.choice() return copy_nut(choice) end + +local function new_hlist(list,width,height,depth,shift) + local n = copy_nut(hlist) + if list then + setlist(n,list) + end + if width or height or depth then + setwhd(n,width,height,depth) + end + if shift and shift ~= 0 then + setshift(n,shift) + end + return n +end + +local function new_vlist(list,width,height,depth,shift) + local n = copy_nut(vlist) + if list then + setlist(n,list) + end + if width or height or depth then + setwhd(n,width,height,depth) + end + if shift and shift ~= 0 then + setshift(n,shift) + end + return n +end + +nutpool.hlist = new_hlist +nutpool.vlist = new_vlist + +function nodepool.hlist(list,width,height,depth,shift) + return tonode(new_hlist(list and tonut(list),width,height,depth,shift)) +end + +function nodepool.vlist(list,width,height,depth,shift) + return tonode(new_vlist(list and tonut(list),width,height,depth,shift)) +end + +function nutpool.usernode(id,data) + local n = copy_nut(user_node) + nodeproperties[n] = { + id = id, + data = data, + } + return n +end + +-- housekeeping + +local function cleanup(nofboxes) -- todo + local tracers = nodes.tracers + if tracers and tracers.steppers then -- to be resolved + tracers.steppers.reset() -- todo: make a registration subsystem + end + local nl = 0 + local nr = nofreserved + for i=1,nofreserved do + local ri = reserved[i] + flush_nut(reserved[i]) + end + if nofboxes then + for i=0,nofboxes do + local l = getbox(i) + if l then + flush_nut(l) -- also list ? + nl = nl + 1 + end + end + end + reserved = { } + nofreserved = 0 + return nr, nl, nofboxes -- can be nil +end + +local usage = node.inuse +local stock = node.instock + +nutpool .cleanup = cleanup +nodepool.cleanup = cleanup + +nutpool .usage = usage +nodepool.usage = usage + +nutpool .stock = stock +nodepool.stock = stock + +-- end + +statistics.register("cleaned up reserved nodes", function() + return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box"))) +end) -- \topofboxstack + +statistics.register("node memory usage", function() -- comes after cleanup ! + local used = usage() + if next(used) then + local t, n = { }, 0 + for k, v in table.sortedhash(used) do + n = n + 1 ; t[n] = format("%s %s",v,k) + end + return table.concat(t,", ") + end +end) + +lua.registerfinalizer(cleanup, "cleanup reserved nodes") + +-- experiment + +do + + local glyph = tonode(glyph) + local traverse_id = nodes.traverse_id + + local traversers = table.setmetatableindex(function(t,k) + local v = traverse_id(type(k) == "number" and k or nodecodes[k],glyph) + t[k] = v + return v + end) + + traversers.node = nodes.traverse (glyph) + traversers.char = nodes.traverse_char (glyph) + if nuts.traverse_glyph then traversers.glyph = nodes.traverse_glyph (glyph) end + if nuts.traverse_list then traversers.list = nodes.traverse_list (glyph) end + + nodes.traversers = traversers + +end + +do + + local glyph = glyph + local traverse_id = nuts.traverse_id + + local traversers = table.setmetatableindex(function(t,k) + local v = traverse_id(type(k) == "number" and k or nodecodes[k],glyph) + t[k] = v + return v + end) + + traversers.node = nuts.traverse (glyph) + traversers.char = nuts.traverse_char (glyph) + if nuts.traverse_glyph then traversers.glyph = nuts.traverse_glyph (glyph) end + if nuts.traverse_list then traversers.list = nuts.traverse_list (glyph) end + if nuts.traverse_content then traversers.content = nuts.traverse_content(glyph) end + + nuts.traversers = traversers + +end diff --git a/tex/context/base/mkxl/node-rul.lmt b/tex/context/base/mkxl/node-rul.lmt new file mode 100644 index 000000000..72a257872 --- /dev/null +++ b/tex/context/base/mkxl/node-rul.lmt @@ -0,0 +1,797 @@ +if not modules then modules = { } end modules ['node-rul'] = { + version = 1.001, + optimize = true, + comment = "companion to node-rul.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this will go to an auxiliary module +-- beware: rules now have a dir field +-- +-- todo: make robust for layers ... order matters + +-- todo: collect successive bit and pieces and combine them +-- +-- path s ; s := shaped(p) ; % p[] has rectangles +-- fill s withcolor .5white ; +-- draw boundingbox s withcolor yellow; + +local tonumber = tonumber + +local context = context +local attributes = attributes +local nodes = nodes +local properties = nodes.properties.data + +local enableaction = nodes.tasks.enableaction + +local nuts = nodes.nuts +local tonode = nuts.tonode +local tonut = nuts.tonut + +local setnext = nuts.setnext +local setprev = nuts.setprev +local setlink = nuts.setlink +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getdirection = nuts.getdirection +local getattr = nuts.getattr +local setattr = nuts.setattr +local getfont = nuts.getfont +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local setwhd = nuts.setwhd +local setattrlist = nuts.setattrlist +local setshift = nuts.setshift +local getwidth = nuts.getwidth +local setwidth = nuts.setwidth +local setoffsets = nuts.setoffsets +local setfield = nuts.setfield + +----- getfield = nuts.getfield +----- getdata = nuts.getdata +local getruledata = nuts.getruledata + +local isglyph = nuts.isglyph + +local flushlist = nuts.flush_list +local effective_glue = nuts.effective_glue +local insert_node_after = nuts.insert_after +local insert_node_before = nuts.insert_before +local find_tail = nuts.tail +local setglue = nuts.setglue +local getrangedimensions = nuts.rangedimensions +local hpack_nodes = nuts.hpack +local copy_list = nuts.copy_list + +local nexthlist = nuts.traversers.hlist + +local nodecodes = nodes.nodecodes +local rulecodes = nodes.rulecodes +local gluecodes = nodes.gluecodes +local listcodes = nodes.listcodes + +local glyph_code = nodecodes.glyph +local par_code = nodecodes.par +local dir_code = nodecodes.dir +local glue_code = nodecodes.glue +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 nodepool = nuts.pool + +local new_rule = nodepool.rule +local new_userrule = nodepool.userrule +local new_kern = nodepool.kern +local new_leader = nodepool.leader + +local n_tostring = nodes.idstostring +local n_tosequence = nodes.tosequence + +local variables = interfaces.variables +local implement = interfaces.implement + +local privateattributes = attributes.private + +local a_ruled = privateattributes('ruled') +local a_runningtext = privateattributes('runningtext') +local a_color = privateattributes('color') +local a_transparency = privateattributes('transparency') +local a_colormodel = privateattributes('colormodel') +local a_linefiller = privateattributes("linefiller") +local a_viewerlayer = privateattributes("viewerlayer") + +local v_both = variables.both +local v_left = variables.left +local v_right = variables.right +local v_local = variables["local"] +local v_yes = variables.yes +local v_foreground = variables.foreground + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontresources = fonthashes.resources + +local dimenfactor = fonts.helpers.dimenfactor +local splitdimen = number.splitdimen +local setmetatableindex = table.setmetatableindex + +local magicconstants = tex.magicconstants +local running = magicconstants.running + +-- + +local striprange = nuts.striprange +local processwords = nuts.processwords + +-- + +local rules = nodes.rules or { } +nodes.rules = rules +rules.data = rules.data or { } + +local nutrules = nuts.rules or { } +nuts.rules = nutrules -- not that many + +storage.register("nodes/rules/data", rules.data, "nodes.rules.data") + +local data = rules.data + +-- we implement user rules here as it takes less code this way + +local function usernutrule(t,noattributes) + local r = new_userrule(t.width or 0,t.height or 0,t.depth or 0) + if noattributes == false or noattributes == nil then + -- avoid fuzzy ones + else + setattrlist(r,true) + end + properties[r] = t + return r +end + +nutrules.userrule = usernutrule + +local function userrule(t,noattributes) + return tonode(usernutrule(t,noattributes)) +end + +rules.userrule = userrule +local ruleactions = { } + +rules .ruleactions = ruleactions +nutrules.ruleactions = ruleactions -- convenient + +local function mathaction(n,h,v,what) + local font = getruledata(n) + local actions = fontresources[font].mathruleactions + if actions then + local action = actions[what] + if action then + action(n,h,v,font) + end + end +end + +local function mathradical(n,h,v) + mathaction(n,h,v,"radicalaction") +end + +local function mathrule(n,h,v) + mathaction(n,h,v,"hruleaction") +end + +local x + +local function useraction(n,h,v) + local p = properties[n] + if p then + local i = p.type or "draw" + local a = ruleactions[i] + if a then + a(p,h,v,i,n) + end + end +end + +local subtypeactions = { + [rulecodes.user] = useraction, + [rulecodes.over] = mathrule, + [rulecodes.under] = mathrule, + [rulecodes.fraction] = mathrule, + [rulecodes.radical] = mathradical, +} + +local function process_rule(n,h,v) + local n = tonut(n) + local s = getsubtype(n) + local a = subtypeactions[s] + if a then + a(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) +local report_ruled = logs.reporter("nodes","rules") + +function rules.define(settings) + local nofdata = #data + 1 + data[nofdata] = settings + local text = settings.text + if text then + local b = nuts.takebox(text) + if b then + nodepool.register(b) + settings.text = getlist(b) + else + settings.text = nil + end + end + return nofdata +end + +local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose + local font = nil + local char, id = isglyph(f) + if char then + font = id + elseif id == hlist_code then + font = getattr(f,a_runningtext) + end + if not font then + -- saveguard ... we need to deal with rules and so (math) + return head + end + local r, m + if strip then + if trace_ruled then + local before = n_tosequence(f,l,true) + f, l = striprange(f,l) + local after = n_tosequence(f,l,true) + report_ruled("range stripper, before %a, after %a",before,after) + else + f, l = striprange(f,l) + end + end + if not f then + return head + end + local wd, ht, dp = getrangedimensions(parent,f,getnext(l)) + local method = d.method + local empty = d.empty == v_yes + local offset = d.offset + local dy = d.dy + local order = d.order + local max = d.max + local mp = d.mp + local rulethickness = d.rulethickness + local unit = d.unit + local ma = d.ma + local ca = d.ca + local ta = d.ta + local colorspace = ma > 0 and ma or getattr(f,a_colormodel) or 1 + local color = ca > 0 and ca or getattr(f,a_color) + local transparency = ta > 0 and ta or getattr(f,a_transparency) + local foreground = order == v_foreground + local layer = getattr(f,a_viewerlayer) + local e = dimenfactor(unit,font) -- what if no glyph node + local rt = tonumber(rulethickness) + if rt then + rulethickness = e * rulethickness / 2 + else + local n, u = splitdimen(rulethickness) + if n and u then -- we need to intercept ex and em and % and ... + rulethickness = n * dimenfactor(u,fontdata[font]) / 2 + else + rulethickness = 1/5 + end + end + -- + if level > max then + level = max + end + if method == 0 then -- center + offset = 2*offset + m = (offset+(level-1)*dy)*e/2 + rulethickness/2 + else + m = 0 + end + + local function inject(r,wd,ht,dp) + if layer then + setattr(r,a_viewerlayer,layer) + end + if empty then + head = insert_node_before(head,f,r) + setlink(r,getnext(l)) + setprev(f) + setnext(l) + flushlist(f) + else + local k = new_kern(-wd) + if foreground then + insert_node_after(head,l,k) + insert_node_after(head,k,r) + l = r + else + head = insert_node_before(head,f,r) + insert_node_after(head,r,k) + end + end + if trace_ruled then + report_ruled("level %a, width %p, height %p, depth %p, nodes %a, text %a", + level,wd,ht,dp,n_tostring(f,l),n_tosequence(f,l,true)) + end + end + + if mp and mp ~= "" then + local r = usernutrule { + width = wd, + height = ht, + depth = dp, + type = "mp", + factor = e, + offset = offset, + line = rulethickness, + data = mp, + ma = colorspace, + ca = color, + ta = transparency, + } + inject(r,wd,ht,dp) + else + local tx = d.text + if tx then + local l = copy_list(tx) + if d["repeat"] == v_yes then + l = new_leader(wd,l) + setattrlist(l,tx) + end + l = hpack_nodes(l,wd,"exactly") + inject(l,wd,ht,dp) + 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) + end + end + end + return head +end + +rules.handler = function(head) + return processwords(a_ruled,data,flush_ruled,head) +end + +function rules.enable() + enableaction("shipouts","nodes.rules.handler") +end + +local trace_shifted = false trackers.register("nodes.shifting", function(v) trace_shifted = v end) + +local report_shifted = logs.reporter("nodes","shifting") + +local a_shifted = attributes.private('shifted') + +local shifts = nodes.shifts or { } +nodes.shifts = shifts +shifts.data = shifts.data or { } + +storage.register("nodes/shifts/data", shifts.data, "nodes.shifts.data") + +local data = shifts.data + +function shifts.define(settings) + local nofdata = #data + 1 + data[nofdata] = settings + return nofdata +end + +local function flush_shifted(head,first,last,data,level,parent,strip) -- not that fast but acceptable for this purpose + if true then + first, last = striprange(first,last) + end + local prev = getprev(first) + local next = getnext(last) + setprev(first) + setnext(last) + local width, height, depth = getrangedimensions(parent,first,next) + local list = hpack_nodes(first,width,"exactly") -- we can use a simple pack + if first == head then + head = list + end + if prev then + setlink(prev,list) + end + if next then + setlink(list,next) + end + local raise = data.dy * dimenfactor(data.unit,fontdata[getfont(first)]) + setshift(list,raise) + setwhd(list,width,height,depth) + if trace_shifted then + report_shifted("width %p, nodes %a, text %a",width,n_tostring(first,last),n_tosequence(first,last,true)) + end + return head +end + +shifts.handler = function(head) + return processwords(a_shifted,data,flush_shifted,head) +end + +function shifts.enable() + enableaction("shipouts","nodes.shifts.handler") +end + +-- linefillers + +local linefillers = nodes.linefillers or { } +nodes.linefillers = linefillers +linefillers.data = linefillers.data or { } + +storage.register("nodes/linefillers/data", linefillers.data, "nodes.linefillers.data") + +local data = linefillers.data + +function linefillers.define(settings) + local nofdata = #data + 1 + data[nofdata] = settings + return nofdata +end + +local function linefiller(current,data,width,location) + local height = data.height + local depth = data.depth + local mp = data.mp + local ma = data.ma + local ca = data.ca + local ta = data.ta + if mp and mp ~= "" then + return usernutrule { + width = width, + height = height, + depth = depth, + type = "mp", + line = data.rulethickness, + data = mp, + ma = ma, + ca = ca, + ta = ta, + option = 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 + end +end + +function linefillers.filler(current,data,width,height,depth) + if width and width > 0 then + local height = height or data.height or 0 + local depth = depth or data.depth or 0 + if (height + depth) ~= 0 then + local mp = data.mp + local ma = data.ma + local ca = data.ca + local ta = data.ta + if mp and mp ~= "" then + return usernutrule { + width = width, + height = height, + depth = depth, + type = "mp", + line = data.rulethickness, + data = mp, + ma = ma, + ca = ca, + ta = ta, + option = 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 + end + end + end +end + +local function find_attr(head,attr) + while head do + local a = head[attr] + if a then + return a, head + end + head = getnext(head) + end +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) + local a = getattr(current,a_linefiller) + if a then + local class = a % 1000 + local data = data[class] + if data then + local location = data.location + local scope = data.scope + local distance = data.distance + local threshold = data.threshold + local leftlocal = false + local rightlocal = false + -- + if scope == v_right then + leftlocal = true + elseif scope == v_left then + rightlocal = true + elseif scope == v_local then + leftlocal = true + 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 width = lefttotal - (leftlocal and leftfixed or 0) + indentation - distance + if width > threshold then + if iskip then + setwidth(iskip,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 + end + 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 + local width = righttotal - (rightlocal and rightfixed or 0) + partotal - distance + if width > threshold then + if pskip then + setglue(pskip) + 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 + end + end + end + end + end + end + end + end + return head +end + +local enable = false + +function linefillers.enable() + if not enable then + -- we could now nil it + enableaction("finalizers","nodes.linefillers.handler") + enable = true + end +end + +-- interface + +implement { + name = "definerule", + actions = { rules.define, context }, + arguments = { + { + { "continue" }, + { "unit" }, + { "order" }, + { "method", "integer" }, + { "offset", "number" }, + { "rulethickness" }, + { "dy", "number" }, + { "max", "number" }, + { "ma", "integer" }, + { "ca", "integer" }, + { "ta", "integer" }, + { "mp" }, + { "empty" }, + { "text", "integer" }, + { "repeat" }, + } + } +} + +implement { + name = "enablerules", + onlyonce = true, + actions = rules.enable +} + +implement { + name = "defineshift", + actions = { shifts.define, context }, + arguments = { + { + { "continue" }, + { "unit" }, + { "method", "integer" }, + { "dy", "number" }, + } + } +} + +implement { + name = "enableshifts", + onlyonce = true, + actions = shifts.enable +} + +implement { + name = "definelinefiller", + actions = { linefillers.define, context }, + arguments = { + { + { "method", "integer" }, + { "location", "string" }, + { "scope", "string" }, + { "mp", "string" }, + { "ma", "integer" }, + { "ca", "integer" }, + { "ta", "integer" }, + { "depth", "dimension" }, + { "height", "dimension" }, + { "distance", "dimension" }, + { "threshold", "dimension" }, + { "rulethickness", "dimension" }, + } + } +} + +implement { + name = "enablelinefillers", + onlyonce = true, + actions = linefillers.enable +} + +-- We add a bonus feature here (experiment): + +interfaces.implement { + name = "autorule", + arguments = { + { + { "width", "dimension" }, + { "height", "dimension" }, + { "depth", "dimension" }, + { "xoffset", "dimension" }, + { "yoffset", "dimension" }, + { "left", "dimension" }, + { "right", "dimension" }, + }, + }, + actions = function(t) + local n = new_rule( + t.width or running, + t.height or running, + t.depth or running + ) + setattrlist(n,true) + setoffsets(n,t.xoffset,t.yoffset) -- ,t.left, t.right + local l = t.left + local r = t.right + if l then + setfield(n,"left",l) + end + if r then + setfield(n,"right",r) + end + context(tonode(n)) + end +} diff --git a/tex/context/base/mkxl/node-rul.mkxl b/tex/context/base/mkxl/node-rul.mkxl index 97417c1d8..a23867804 100644 --- a/tex/context/base/mkxl/node-rul.mkxl +++ b/tex/context/base/mkxl/node-rul.mkxl @@ -71,7 +71,7 @@ %definesystemattribute[ruled] %definesystemattribute[shifted] -\registerctxluafile{node-rul}{optimize} +\registerctxluafile{node-rul}{autosuffix,optimize} \installcorenamespace{bar} \installcorenamespace{barindex} diff --git a/tex/context/base/mkxl/node-scn.lmt b/tex/context/base/mkxl/node-scn.lmt new file mode 100644 index 000000000..0ec1ba387 --- /dev/null +++ b/tex/context/base/mkxl/node-scn.lmt @@ -0,0 +1,314 @@ +if not modules then modules = { } end modules ['node-scn'] = { + 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 floor = math.floor + +local attributes = attributes +local nodes = nodes + +local nuts = nodes.nuts + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getattr = nuts.getattr +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local setlist = nuts.setlist + +local end_of_math = nuts.end_of_math + +local nodecodes = nodes.nodecodes +local gluecodes = nodes.gluecodes +local kerncodes = nodes.kerncodes + +local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc +local rule_code = nodecodes.rule +local boundary_code = nodecodes.boundary +local dir_code = nodecodes.dir +local math_code = nodecodes.math +local glue_code = nodecodes.glue +local penalty_code = nodecodes.penalty +local kern_code = nodecodes.kern +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist + +local userskip_code = gluecodes.userskip +local spaceskip_code = gluecodes.spaceskip +local xspaceskip_code = gluecodes.xspaceskip +local leaders_code = gluecodes.leaders + +local fontkern_code = kerncodes.fontkern + +local variables = interfaces.variables + +local privateattributes = attributes.private + +local a_runningtext = privateattributes('runningtext') + +local v_yes = variables.yes +local v_all = variables.all + +local function striprange(first,last) -- todo: dir + if first and last then -- just to be sure + if first == last then + return first, last + end + while first and first ~= last do + local id = getid(first) + if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code + break + else + first = getnext(first) + end + end + if not first then + return nil, nil + elseif first == last then + return first, last + end + while last and last ~= first do + local id = getid(last) + if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code + break + else + local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd + if prev then + last = prev + else + break + end + end + end + if not last then + return nil, nil + end + end + return first, last +end + +nuts.striprange = striprange + +-- todo: order and maybe other dimensions + +-- we can use this one elsewhere too +-- +-- todo: functions: word, sentence +-- +-- glyph rule unset whatsit glue margin_kern kern math disc + +-- we assume {glyphruns} and no funny extra kerning, ok, maybe we need +-- a dummy character as start and end; anyway we only collect glyphs +-- +-- this one needs to take layers into account (i.e. we need a list of +-- critical attributes) + +-- omkeren class en level -> scheelt functie call in analyze + +-- todo: switching inside math + +-- handlers + +local function processwords(attribute,data,flush,head,parent,skip) -- we have hlistdir and local dir + local n = head + if n then + local f, l, a, d, i, class + local continue, leaders, done, strip, level = false, false, false, true, -1 + while n do + local id = getid(n) + if id == glyph_code or id == rule_code or (id == hlist_code and getattr(n,a_runningtext)) then + local aa = getattr(n,attribute) + if aa and aa ~= skip then + if aa == a then + if not f then -- ? + f = n + end + l = n + else + -- possible extensions: when in same class then keep spanning + local newlevel, newclass = floor(aa/1000), aa%1000 -- will be configurable + -- strip = not continue or level == 1 -- 0 + if f then + if class == newclass then -- and newlevel > level then + head, done = flush(head,f,l,d,level,parent,false), true + else + head, done = flush(head,f,l,d,level,parent,strip), true + end + end + f, l, a = n, n, aa + level, class = newlevel, newclass + d = data[class] + if d then + local c = d.continue + leaders = c == v_all + continue = leaders or c == v_yes + else + continue = true + end + end + else + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + end + f, l, a = nil, nil, nil + end + if id == hlist_code then + local list = getlist(n) + if list then + setlist(n,(processwords(attribute,data,flush,list,n,aa))) -- watch () + end + end + elseif id == disc_code or id == boundary_code then + if f then + l = n + end + elseif id == kern_code and getsubtype(n) == fontkern_code then + if f then + l = n + end + elseif id == math_code then + -- otherwise not consistent: a $b$ c vs a $b+c$ d etc + -- we need a special (optional) go over math variant + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + elseif id == hlist_code or id == vlist_code then + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + local list = getlist(n) + if list then + setlist(n,(processwords(attribute,data,flush,list,n,skip))) -- watch () + end + elseif id == dir_code then -- only changes in dir, we assume proper boundaries + if f then + l = n + end + elseif f then + if continue then + if id == penalty_code then + l = n + -- elseif id == kern_code then + -- l = n + elseif id == glue_code then + -- catch \underbar{a} \underbar{a} (subtype test is needed) + local subtype = getsubtype(n) + if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code or (leaders and subtype >= leaders_code)) then + l = n + else + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + end + else + head, done = flush(head,f,l,d,level,parent,strip), true + f, l, a = nil, nil, nil + end + end + n = getnext(n) + end + if f then + head, done = flush(head,f,l,d,level,parent,strip), true + end + return head, true -- todo: done + else + return head, false + end +end + +nuts.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir + return processwords(attribute,data,flush,head,parent) +end + +-- works on lines ! +-- todo: stack because skip can change when nested + +local function processranges(attribute,flush,head,parent,depth,skip) + local n = head + if n then + local f, l, a + local done = false + while n do + local id = getid(n) + if id == glyph_code or id == rule_code then + local aa = getattr(n,attribute) +-- if aa and (not skip or aa ~= skip) then + if aa then + if aa == a then + if not f then + f = n + end + l = n + else + if f then + head, done = flush(head,f,l,a,parent,depth), true + end + f, l, a = n, n, aa + end + else + if f then + head, done = flush(head,f,l,a,parent,depth), true + end + f, l, a = nil, nil, nil + end + elseif id == disc_code or id == boundary_code then + if f then + l = n + else + -- weird + end + elseif id == kern_code and getsubtype(n) == fontkern_code then + if f then + l = n + end + -- elseif id == penalty_code then + elseif id == glue_code then + -- todo: leaders + elseif id == hlist_code or id == vlist_code then + local aa = getattr(n,attribute) +-- if aa and (not skip or aa ~= skip) then + if aa then + if aa == a then + if not f then + f = n + end + l = n + else + if f then + head, done = flush(head,f,l,a,parent,depth), true + end + f, l, a = n, n, aa + end + else + if f then + head, done = flush(head,f,l,a,parent,depth), true + end + f, l, a = nil, nil, nil + end + local list = getlist(n) + if list then + setlist(n,(processranges(attribute,flush,list,n,depth+1,aa))) + end + end + n = getnext(n) + end + if f then + head, done = flush(head,f,l,a,parent,depth), true + end + return head, done + else + return head, false + end +end + +nuts.processranges = function(attribute,flush,head,parent) -- we have hlistdir and local dir + return processranges(attribute,flush,head,parent,0) +end diff --git a/tex/context/base/mkxl/node-shp.lmt b/tex/context/base/mkxl/node-shp.lmt new file mode 100644 index 000000000..64508ecc7 --- /dev/null +++ b/tex/context/base/mkxl/node-shp.lmt @@ -0,0 +1,115 @@ +if not modules then modules = { } end modules ['node-shp'] = { + version = 1.001, + optimize = true, + 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 next, type = next, type +local format = string.format +local setmetatableindex = table.setmetatableindex + +local nodes = nodes +local tasks = nodes.tasks +local handlers = nodes.handlers + +local nodecodes = nodes.nodecodes +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist + +local nuts = nodes.nuts +local tonut = nuts.tonut + +local nextnode = nuts.traversers.node +local getlist = nuts.getlist +local getbox = nuts.getbox + +local implement = interfaces.implement + +local actions = tasks.actions("shipouts") + +function handlers.finalizebox(box) + actions(getbox(box)) -- nut +end + +handlers.cleanuppage = nuts.flatten_discretionaries + +-- interface + +implement { + name = "finalizebox", + arguments = "integer", + actions = handlers.finalizebox, +} + +-- just in case we want to optimize lookups: + +local frequencies = { } + +nodes.tracers.frequencies = frequencies + +local data = { } +local done = false + +setmetatableindex(data,function(t,k) + local v = { } + setmetatableindex(v,function(t,k) + local v = { } + t[k] = v + setmetatableindex(v,function(t,k) + t[k] = 0 + return 0 + end) + return v + end) + t[k] = v + return v +end) + +local function count(head,data,subcategory) + -- no components, pre, post, replace .. can maybe an option .. but + -- we use this for optimization so it makes sense to look the the + -- main node only + for n, id in nextnode, tonut(head) do + local dn = data[nodecodes[id]] -- we could use id and then later convert to nodecodes + dn[subcategory] = dn[subcategory] + 1 + if id == hlist_code or id == vlist_code then + count(getlist(n),data,subcategory) + end + end +end + +local function register(category,subcategory) + return function(head) + done = true + count(head,data[category],subcategory) + return head, false + end +end + +frequencies.register = register +frequencies.filename = nil + +trackers.register("nodes.frequencies",function(v) + if type(v) == "string" then + frequencies.filename = v + end + handlers.frequencies_shipouts_before = register("shipouts", "begin") + handlers.frequencies_shipouts_after = register("shipouts", "end") + handlers.frequencies_processors_before = register("processors", "begin") + handlers.frequencies_processors_after = register("processors", "end") + tasks.prependaction("shipouts", "before", "nodes.handlers.frequencies_shipouts_before") + tasks.appendaction ("shipouts", "after", "nodes.handlers.frequencies_shipouts_after") + tasks.prependaction("processors", "before", "nodes.handlers.frequencies_processors_before") + tasks.appendaction ("processors", "after", "nodes.handlers.frequencies_processors_after") +end) + +statistics.register("node frequencies", function() + if done then + local filename = frequencies.filename or (tex.jobname .. "-frequencies.lua") + io.savedata(filename,table.serialize(data,true)) + return format("saved in %q",filename) + end +end) diff --git a/tex/context/base/mkxl/page-imp.mkxl b/tex/context/base/mkxl/page-imp.mkxl index 1d2ef5072..1e8586be5 100644 --- a/tex/context/base/mkxl/page-imp.mkxl +++ b/tex/context/base/mkxl/page-imp.mkxl @@ -196,13 +196,7 @@ \finalizeshipoutbox\shipoutscratchbox \fi \setbox\shipoutscratchbox\vpack - {\scratchdimen\clf_shipoutoffset\relax - \ifdim\scratchdimen=\zeropoint\else - \offinterlineskip - \vkern\scratchdimen - \hkern\scratchdimen - \fi - \hpack + {\hpack {\page_otr_flush_every_stuff \page_otr_flush_special_content \box\shipoutscratchbox}}% diff --git a/tex/context/base/mkxl/page-lin.lmt b/tex/context/base/mkxl/page-lin.lmt new file mode 100644 index 000000000..9ea502899 --- /dev/null +++ b/tex/context/base/mkxl/page-lin.lmt @@ -0,0 +1,418 @@ +if not modules then modules = { } end modules ['page-lin'] = { + version = 1.001, + comment = "companion to page-lin.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, tonumber = next, tonumber + +local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end) + +local report_lines = logs.reporter("lines") + +local attributes = attributes +local nodes = nodes +local context = context + +local implement = interfaces.implement + +nodes.lines = nodes.lines or { } +local lines = nodes.lines + +lines.data = lines.data or { } -- start step tag +local data = lines.data +local last = #data + +storage.register("lines/data", data, "nodes.lines.data") + +local variables = interfaces.variables + +local v_next = variables.next +local v_page = variables.page +local v_no = variables.no + +local properties = nodes.properties.data + +local nodecodes = nodes.nodecodes +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local whatsit_code = nodecodes.whatsit +local glyph_code = nodecodes.glyph + +local linelist_code = nodes.listcodes.line + +local latelua_code = nodes.whatsitcodes.latelua + +local localtexrun = tex.runlocal + +----- a_displaymath = attributes.private('displaymath') +local a_linenumber = attributes.private('linenumber') +local a_linereference = attributes.private('linereference') + +local cross_references = { } + +local nuts = nodes.nuts + +local getid = nuts.getid +local getattr = nuts.getattr +local getlist = nuts.getlist +local getbox = nuts.getbox +local gettotal = nuts.gettotal + +local takebox = nuts.takebox + +local nexthlist = nuts.traversers.hlist +local nextvlist = nuts.traversers.vlist +local nextcontent = nuts.traversers.content +local nextlist = nuts.traversers.list +local nextnode = nuts.traversers.node +local nextwhatsit = nuts.traversers.whatsit + +local is_display_math = nuts.is_display_math + +local ctx_convertnumber = context.convertnumber +local ctx_makelinenumber = context.makelinenumber + +local paragraphs = typesetters.paragraphs +local addtoline = paragraphs.addtoline +local checkline = paragraphs.checkline + +-- cross referencing + +function lines.number(n) + n = tonumber(n) + local cr = cross_references[n] or 0 + cross_references[n] = nil + return cr +end + +function lines.finalize(t) + local getnumber = lines.number + for _,p in next, t do + for _,r in next, p do + local m = r.metadata + if m and m.kind == "line" then + local e = r.entries + local u = r.userdata + e.linenumber = getnumber(e.text or 0) -- we can nil e.text + e.conversion = u and u.conversion + r.userdata = nil -- hack + end + end + end +end + +local filters = structures.references.filters +local helpers = structures.helpers + +structures.references.registerfinalizer(lines.finalize) + +filters.line = filters.line or { } + +function filters.line.default(data) + ctx_convertnumber(data.entries.conversion or "numbers",data.entries.linenumber or "0") +end + +function filters.line.page(data,prefixspec,pagespec) -- redundant + helpers.prefixpage(data,prefixspec,pagespec) +end + +function filters.line.linenumber(data) -- raw + context(data.entries.linenumber or "0") +end + +-- boxed variant, todo: use number mechanism + +local boxed = lines.boxed or { } +lines.boxed = boxed + +-- todo: cache setups, and free id no longer used +-- use interfaces.cachesetup(t) + +function boxed.register(configuration) + last = last + 1 + data[last] = configuration + if trace_numbers then + report_lines("registering setup %a",last) + end + return last +end + +implement { + name = "registerlinenumbering", + actions = { boxed.register, context }, + arguments = { + { + { "continue" }, + { "start", "integer" }, + { "step", "integer" }, + { "method" }, + { "tag" }, + } + } +} + +function boxed.setup(n,configuration) + local d = data[n] + if d then + if trace_numbers then + report_lines("updating setup %a",n) + end + for k,v in next, configuration do + d[k] = v + end + else + if trace_numbers then + report_lines("registering setup %a (br)",n) + end + data[n] = configuration + end + return n +end + +implement { + name = "setuplinenumbering", + actions = boxed.setup, + arguments = { + "integer", + { + { "continue" }, + { "start", "integer" }, + { "step", "integer" }, + { "method" }, + { "tag" }, + } + } +} + +local function resolve(n,m) -- we can now check the 'line' flag (todo) + for current, id, subtype, content in nextlist, n do + if content then + if id == hlist_code then + for current, subtype in nextwhatsit, content do + if subtype == latelua_code then + local a = getattr(current,a_linereference) + if a then + cross_references[a] = m + end + end + end + end + resolve(content,m) + end + end +end + +local function check_number(n,b,a,skip) + local d = data[a] + if d then + local tag = d.tag or "" + local s = d.start or 1 + local okay = not skip and s % d.step == 0 + if trace_numbers then + report_lines("%s number for setup %a, tag %a, line %s, continued %a",okay and "making" or "skipping",a,tag,s,d.continue or v_no) + end + if okay then + local p = checkline(n) + if p then + localtexrun(function() ctx_makelinenumber(tag,s,p.hsize,p.reverse and 1 or 0) end) + local l = takebox(b) + if l then + addtoline(n,l) + else + -- very unlikely + end + end + end + resolve(n,s) + d.start = s + 1 + end +end + +-- print(nodes.idstostring(list)) + +-- hlists of type line will only have an attribute when the line number attribute +-- still set at par building time which is not always the case unless we explicitly +-- do a par before we end the line + +local function lineisnumbered(n) -- content node + for n, id, subtype, content in nextcontent, getlist(n) do + local a = getattr(n,a_linenumber) + if a and a > 0 then + return a + end + end +end + +local function listisnumbered(list) + if list then + for n, subtype in nexthlist, list do + if subtype == linelist_code then + local a = getattr(n,a_linenumber) + if a then + -- a quick test for lines (only valid when \par before \stoplinenumbering) + return a > 0 and list or false + else + -- a bit slower one, assuming that we have normalized and anchored + if lineisnumbered(n) then + return list + end + end + end + end + end +end + +local function findnumberedlist(list) + -- we assume wrapped boxes, only one with numbers + for n, id, subtype, content in nextcontent, list do + if id == hlist_code then + if subtype == linelist_code then + local a = getattr(n,a_linenumber) + if a then + return a > 0 and list + end + return + else + if lineisnumbered(content) then + return n + end + local okay = findnumberedlist(content) + if okay then + return okay + end + end + elseif id == vlist_code then + if listisnumbered(content) then + return content + end + local okay = findnumberedlist(content) + if okay then + return okay + end + elseif id == glyph_code then + return + end + end +end + +-- reset ranges per page +-- store first and last per page +-- maybe just set marks directly + +local function findcolumngap(list) + -- we assume wrapped boxes, only one with numbers + for n, id, subtype, content in nextlist, list do + if id == hlist_code or id == vlist_code then + local p = properties[n] + if p and p.columngap then + if trace_numbers then + report_lines("first column gap %a",p.columngap) + end + return n + elseif content then + local okay = findcolumngap(content) + if okay then + return okay + end + end + end + end +end + +function boxed.addlinenumbers(n,b,nested) + local box = getbox(n) + if not box then + return + end + local list = getlist(box) + if not list then + return + end + local last_a = nil + local last_v = -1 + local skip = false + + local function check() + for n, subtype in nexthlist, list do + if subtype ~= linelist_code then + -- go on + elseif gettotal(n) == 0 then + -- skip funny hlists -- todo: check line subtype + else + local a = lineisnumbered(n) + if a then + if last_a ~= a then + local da = data[a] + local ma = da.method + if ma == v_next then + skip = true + elseif ma == v_page then + da.start = 1 -- eventually we will have a normal counter + end + last_a = a + if trace_numbers then + report_lines("starting line number range %s: start %s, continue %s",a,da.start,da.continue or v_no) + end + end + -- if getattr(n,a_displaymath) then + -- check_number(n,b,a,skip) + -- else + check_number(n,b,a,skip) + -- end + skip = false + end + end + end + end + + if nested == 0 then + if list then + check() + end + elseif nested == 1 then + local id = getid(box) + if id == vlist_code then + if listisnumbered(list) then + -- ok + else + list = findnumberedlist(list) + end + else -- hlist + list = findnumberedlist(list) + end + if list then + check() + end + elseif nested == 2 then + list = findcolumngap(list) + -- we assume we have a vlist + if not list then + return + end + for n in nextvlist, list do + local p = properties[n] + if p and p.columngap then + if trace_numbers then + report_lines("found column gap %a",p.columngap) + end + list = getlist(n) + if list then + check() + end + end + end + else + -- bad call + end +end + +-- column attribute + +implement { + name = "addlinenumbers", + actions = boxed.addlinenumbers, + arguments = { "integer", "integer", "integer" } +} diff --git a/tex/context/base/mkxl/page-lin.mklx b/tex/context/base/mkxl/page-lin.mklx index fc911191d..3b3a3d71b 100644 --- a/tex/context/base/mkxl/page-lin.mklx +++ b/tex/context/base/mkxl/page-lin.mklx @@ -40,7 +40,7 @@ % % some line -\registerctxluafile{page-lin}{} +\registerctxluafile{page-lin}{autosuffix} \definesystemattribute[linenumber] [public] \definesystemattribute[linereference][public] @@ -72,7 +72,7 @@ % tag skipflag s getfield(n,"shift") getfield(n,"width") leftmarginwidth(getlist(n)) getfield(n,"dir")) -\let\makelinenumber\gobblefivearguments % used at lua end +\permanent\let\makelinenumber\gobblefivearguments % used at lua end \newconditional\page_postprocessors_needed_box @@ -349,26 +349,16 @@ \c_page_lines_column #column\relax \c_page_lines_last_column#max\relax \fullrestoreglobalbodyfont - \let\makelinenumber\page_lines_make_number % used at lua end - \setbox\b_page_lines_scratch\vbox - {\forgetall - \offinterlineskip - \clf_linenumbersstageone - \b_page_lines_number - #nesting% - \relax}% - \clf_linenumbersstagetwo + \enforced\let\makelinenumber\page_lines_make_number % used at lua end + \clf_addlinenumbers \b_page_lines_number - \b_page_lines_scratch + \scratchbox + #nesting% \relax \egroup} -\def\page_lines_make_number#tag#mode#linenumber#width#dir% with hang and parindent and skips we have to compensate for \hsize - {\naturalhbox to \zeropoint \bgroup - \ifcase#mode\relax - % \settrue \c_page_lines_fake_number - \else - % \setfalse\c_page_lines_fake_number +\def\page_lines_make_number#tag#linenumber#width#dir% with hang and parindent and skips we have to compensate for \hsize + {\setbox\scratchbox\naturalhbox to \zeropoint \bgroup \edef\currentlinenumbering{#tag}% \def\linenumber{#linenumber}% unsafe \d_page_lines_line_width#width\scaledpoint\relax @@ -400,8 +390,6 @@ \page_line_swap_align % can become a helper \fi \fi - % \else - % too fuzzy \fi \ifx\p_location\v!default \ifconditional\c_page_lines_dir_left_to_right @@ -413,7 +401,6 @@ \fi % \begincsname\??linenumberinghandler\p_location\endcsname - \fi \egroup} \def\page_lines_number_inject#align#width% diff --git a/tex/context/base/mkxl/supp-box.lmt b/tex/context/base/mkxl/supp-box.lmt index 41013da9a..53ad3ae31 100644 --- a/tex/context/base/mkxl/supp-box.lmt +++ b/tex/context/base/mkxl/supp-box.lmt @@ -890,6 +890,7 @@ implement { actions = function() local n = texgetnest() local b = false + -- can be a helper ! if n.mode == hmode_code then n = tonut(n.head) while n do diff --git a/tex/context/base/mkxl/type-ini.lmt b/tex/context/base/mkxl/type-ini.lmt new file mode 100644 index 000000000..23d5096e2 --- /dev/null +++ b/tex/context/base/mkxl/type-ini.lmt @@ -0,0 +1,45 @@ +if not modules then modules = { } end modules ['type-ini'] = { + version = 1.001, + comment = "companion to type-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs + +local context = context +local implement = interfaces.implement +local uselibrary = resolvers.uselibrary +local p_strip = Cs((P("type-") * (P("imp-")^0))^0/"" * P(1)^0) +local report = logs.reporter("fonts","typescripts") + +local function action(name,foundname) + context.loadfoundtypescriptfile(name,foundname) +end + +local patterns = { + "type-imp-%s.mkxl", + "type-imp-%s.mkiv", + "type-imp-%s.tex" +} + +local function failure(name) + if name ~= "loc" then + report("unknown library %a",name) + end +end + +implement { + name = "loadtypescriptfile", + arguments = "string", + actions = function(name) -- a more specific name + uselibrary { + name = lpegmatch(p_strip,name) or name, + patterns = patterns, + action = action, + failure = failure, + onlyonce = false, -- will become true + } + end +} diff --git a/tex/context/base/mkxl/type-ini.mklx b/tex/context/base/mkxl/type-ini.mklx index fa616e0f7..0e1c4340e 100644 --- a/tex/context/base/mkxl/type-ini.mklx +++ b/tex/context/base/mkxl/type-ini.mklx @@ -13,7 +13,7 @@ \writestatus{loading}{ConTeXt Typescript Macros / Initialization} -\registerctxluafile{type-ini}{} +\registerctxluafile{type-ini}{autosuffix} %D The default fontclass is empty. We could demand always using fontclasses, and %D then make the calling macros simplier (always assume fontclass) but apart from @@ -240,7 +240,7 @@ \letcsname\??typescriptfiles\currenttypefile\endcsname\t_font_typescripts} \def\font_typescript_process_typescript_file - {\clf_doprocesstypescriptfile{\currenttypefile}} + {\clf_loadtypescriptfile{\currenttypefile}} \permanent\tolerant\protected\def\usetypescriptonce[#one]#spacer[#two]#spacer[#three]% {\ifcsname\??typescriptonce#one:#two:#three\endcsname diff --git a/tex/context/base/mkxl/typo-lin.lmt b/tex/context/base/mkxl/typo-lin.lmt index e49f13b60..86365c3ba 100644 --- a/tex/context/base/mkxl/typo-lin.lmt +++ b/tex/context/base/mkxl/typo-lin.lmt @@ -58,17 +58,11 @@ local report = logs.reporter("anchors") local nuts = nodes.nuts local nodecodes = nodes.nodecodes -local gluecodes = nodes.gluecodes local listcodes = nodes.listcodes local hlist_code = nodecodes.hlist -local glue_code = nodecodes.glue local kern_code = nodecodes.kern local linelist_code = listcodes.line ------ par_code = nodecodes.par -local leftskip_code = gluecodes.leftskip -local rightskip_code = gluecodes.rightskip -local parfillskip_code = gluecodes.parfillskip local tonut = nodes.tonut local tonode = nodes.tonode @@ -76,16 +70,10 @@ local tonode = nodes.tonode local nexthlist = nuts.traversers.hlist local insert_before = nuts.insert_before local insert_after = nuts.insert_after -local find_tail = nuts.tail -local rehpack = nuts.rehpack ------ remove_node = nuts.remove -local getsubtype = nuts.getsubtype local getlist = nuts.getlist local setlist = nuts.setlist local getid = nuts.getid -local getnext = nuts.getnext -local getprev = nuts.getprev local getboth = nuts.getboth local setlink = nuts.setlink local setkern = nuts.setkern @@ -96,6 +84,8 @@ local setshift = nuts.setshift local getwidth = nuts.getwidth local setwidth = nuts.setwidth +local getnormalizedline = nuts.getnormalizedline + local setprop = nuts.setprop local getprop = nuts.rawprop -- getprop @@ -103,8 +93,6 @@ local effectiveglue = nuts.effective_glue local nodepool = nuts.pool local new_kern = nodepool.kern -local new_leftskip = nodepool.leftskip -local new_rightskip = nodepool.rightskip local new_hlist = nodepool.hlist local new_rule = nodepool.rule local new_glue = nodepool.glue @@ -112,9 +100,7 @@ local new_glue = nodepool.glue local righttoleft_code = nodes.dirvalues.righttoleft local texgetcount = tex.getcount -local texgetglue = tex.getglue local setmetatableindex = table.setmetatableindex -local formatters = string.formatters local jobpositions = job.positions local getposition = jobpositions.get @@ -123,14 +109,8 @@ local getreserved = jobpositions.getreserved local paragraphs = { } typesetters.paragraphs = paragraphs -local addskips = false -- todo: use engine normalizer local noflines = 0 --- This is the third version, a mix between immediate (prestine lines) and delayed --- as we don't want anchors that are not used. - --- I will make a better variant once lmtx is stable i.e. less clutter. - local function finalize(prop,key) -- delayed calculations local line = prop.line local hsize = prop.hsize @@ -147,7 +127,7 @@ local function finalize(prop,key) -- delayed calculations end local kern1 = new_kern(delta) local kern2 = new_kern(-delta) - head = insert_before(head,head,kern1) + head = insert_before(head,head,kern1) -- setlink head = insert_before(head,head,pack) head = insert_before(head,head,kern2) setlist(line,head) @@ -158,75 +138,36 @@ local function finalize(prop,key) -- delayed calculations } prop.where = where prop.reverse = reverse - prop.shift = shift + -- prop.shift = shift setmetatableindex(prop,nil) return prop[key] end local function normalize(line,islocal) -- assumes prestine lines, nothing pre/appended - local oldhead = getlist(line) - local head = oldhead - local leftskip = nil - local rightskip = nil - local width = getwidth(line) - local hsize = islocal and width or tex.hsize - local lskip = 0 - local rskip = 0 - local pskip = 0 - local current = head - local id = getid(current) - if id == glue_code then - local subtype = getsubtype(head) - if subtype == leftskip_code then - leftskip = head - lskip = getwidth(head) or 0 - end - current = getnext(head) - id = getid(current) - end - -- no: - -- if id == par_code then - -- head = remove_node(head,head,true) - -- end - local tail = find_tail(head) - local current = tail - local id = getid(current) - if id == glue_code then - if getsubtype(current) == rightskip_code then - rightskip = tail - rskip = getwidth(current) or 0 - current = getprev(tail) - id = getid(current) - end - if id == glue_code then - if getsubtype(current) == parfillskip_code then - pskip = effectiveglue(current,line) - end - end - end - if addskips then - if rightskip and not leftskip then - leftskip = new_leftskip(lskip) - head = insert_before(head,head,leftskip) - end - if leftskip and not rightskip then - rightskip = new_rightskip(0) - head, tail = insert_after(head,tail,rightskip) - end - end - if head ~= oldhead then - setlist(line,head) + local prop = getnormalizedline(line) -- we also can have "lineproperties" set but for a different porpose + local width = getwidth(line) + local hsize = islocal and width or tex.hsize + noflines = noflines + 1 + if prop then + prop.width = width + prop.hsize = hsize + prop.line = line + prop.number = noflines + else + -- can be a vbox ... we could use a metatable with zeros + prop = { + width = width, + hsize = hsize, + leftskip = 0, + rightskip = 0, + lefthangskip = 0, + righthangskip = 0, + parfillleftskip = 0, + parfillrightskip = 0, + line = line, + number = noflines, + } end - noflines = noflines + 1 - local prop = { - width = width, - hsize = hsize, - leftskip = lskip, - rightskip = rskip, - parfillskip = pskip, - line = line, - number = noflines, - } setprop(line,"line",prop) setmetatableindex(prop,finalize) return prop @@ -236,22 +177,6 @@ function paragraphs.checkline(n) return getprop(n,"line") or normalize(n,true) end --- do we still need this: - -function paragraphs.normalize(head,islocal) - if texgetcount("pagebodymode") > 0 then - -- can be an option, maybe we need a proper state in lua itself ... is this check still needed? - return head, false - end - -- normalizer : todo, get the data, no need to normalize - for line, subtype in nexthlist, head do - if subtype == linelist_code and not getprop(line,"line") then - normalize(line) - end - end - return head, true -- true is obsolete -end - -- print(nodes.idstostring(head)) -- We do only basic positioning and leave compensation for directions and distances @@ -284,9 +209,9 @@ local function addtoline(n,list,option) local delta = 0 if option == "internal" then if line.reverse then - delta = line.shift - line.leftskip - (line.hsize - line.width) + delta = line.lefthangskip - line.leftskip - (line.hsize - line.width) else - delta = line.shift + line.leftskip + delta = line.lefthangskip + line.leftskip end end -- always kerns, also when 0 so that we can adapt but we can optimize if needed @@ -372,7 +297,7 @@ function paragraphs.moveinline(n,blob,dx,dy) end end else --- report("no line") + -- report("no line") end end end @@ -389,6 +314,8 @@ local function setanchor(h_anchor) } end +-- needs to be adapted ! + function paragraphs.calculatedelta(n,width,delta,atleft,islocal,followshape,area) local line = type(n) ~= "table" and getprop(n,"line") or n if not line then @@ -427,18 +354,18 @@ function paragraphs.calculatedelta(n,width,delta,atleft,islocal,followshape,area end end if followshape then - -- shape compensation + -- shape compensation (compare to mkiv where we have shift!) if atleft then if reverse then - delta = delta + line.shift - line.hsize + line.width + delta = delta + line.lefthangskip - line.hsize + line.width -- needs testing: + line.parfillleftskip else - delta = delta + line.shift + delta = delta + line.lefthangskip -- needs testing: - line.parfillleftskip end else if reverse then - delta = delta + line.shift + line.parfillskip + delta = delta + line.lefthangskip + line.parfillrightskip else - delta = delta + line.shift - line.hsize + line.width - line.parfillskip + delta = delta + line.lefthangskip - line.hsize + line.width - line.parfillrightskip end end end @@ -449,7 +376,7 @@ function paragraphs.calculatedelta(n,width,delta,atleft,islocal,followshape,area addanchortoline(line,setanchor(number)) line.hanchor = true end - local blob = getposition(s_anchor,number) + local blob = getposition("md:h",number) if blob then local reference = getreserved(area,blob.c) if reference then diff --git a/tex/context/base/mkxl/typo-mar.lmt b/tex/context/base/mkxl/typo-mar.lmt new file mode 100644 index 000000000..7e69162a9 --- /dev/null +++ b/tex/context/base/mkxl/typo-mar.lmt @@ -0,0 +1,995 @@ +if not modules then modules = { } end modules ['typo-mar'] = { + version = 1.001, + comment = "companion to typo-mar.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: +-- +-- * autoleft/right depending on available space (or distance to margin) +-- * floating margin data, with close-to-call anchoring + +local format, validstring = string.format, string.valid +local insert, remove, sortedkeys, fastcopy = table.insert, table.remove, table.sortedkeys, table.fastcopy +local setmetatable, next, tonumber = setmetatable, next, tonumber +local formatters = string.formatters +local toboolean = toboolean +local settings_to_hash = utilities.parsers.settings_to_hash + +local attributes = attributes +local nodes = nodes +local variables = variables +local context = context + +local trace_margindata = false trackers.register("typesetters.margindata", function(v) trace_margindata = v end) +local trace_marginstack = false trackers.register("typesetters.margindata.stack", function(v) trace_marginstack = v end) +local trace_margingroup = false trackers.register("typesetters.margindata.group", function(v) trace_margingroup = v end) + +local report_margindata = logs.reporter("margindata") + +local tasks = nodes.tasks +local prependaction = tasks.prependaction +local disableaction = tasks.disableaction +local enableaction = tasks.enableaction + +local variables = interfaces.variables + +local conditionals = tex.conditionals +local systemmodes = tex.systemmodes + +local v_top = variables.top +local v_depth = variables.depth +local v_local = variables["local"] +local v_global = variables["global"] +local v_left = variables.left +local v_right = variables.right +local v_inner = variables.inner +local v_outer = variables.outer +local v_margin = variables.margin +local v_edge = variables.edge +local v_default = variables.default +local v_normal = variables.normal +local v_yes = variables.yes +local v_continue = variables.continue +local v_first = variables.first +local v_text = variables.text +local v_paragraph = variables.paragraph +local v_line = variables.line + +local nuts = nodes.nuts +local tonode = nuts.tonode + +local hpack_nodes = nuts.hpack +local traverse_id = nuts.traverse_id +local flush_node_list = nuts.flush_list + +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getattr = nuts.getattr +local setattr = nuts.setattr +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local getwhd = nuts.getwhd +local setlist = nuts.setlist +local setlink = nuts.setlink +local getshift = nuts.getshift +local setshift = nuts.setshift +local getwidth = nuts.getwidth +local setwidth = nuts.setwidth +local getheight = nuts.getheight + +local setattrlist = nuts.setattrlist + +local getbox = nuts.getbox +local takebox = nuts.takebox + +local setprop = nuts.setprop +local getprop = nuts.getprop + +local nodecodes = nodes.nodecodes +local listcodes = nodes.listcodes +local whatsitcodes = nodes.whatsitcodes + +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local whatsit_code = nodecodes.whatsit +local userdefined_code = whatsitcodes.userdefined + +local nodepool = nuts.pool + +local new_hlist = nodepool.hlist +local new_usernode = nodepool.usernode +local latelua = nodepool.latelua + +local texgetdimen = tex.getdimen +local texgetcount = tex.getcount +local texget = tex.get + +local isleftpage = layouts.status.isleftpage +local registertogether = builders.paragraphs.registertogether + +local paragraphs = typesetters.paragraphs +local addtoline = paragraphs.addtoline +local moveinline = paragraphs.moveinline +local calculatedelta = paragraphs.calculatedelta + +local a_linenumber = attributes.private('linenumber') + +local inline_mark = nodepool.userids["margins.inline"] + +local jobpositions = job.positions +local getposition = jobpositions.get +local setposition = jobpositions.set +local getreserved = jobpositions.getreserved + +local margins = { } +typesetters.margins = margins + +local locations = { v_left, v_right, v_inner, v_outer } -- order might change +local categories = { } +local displaystore = { } -- [category][location][scope] +local inlinestore = { } -- [number] +local nofsaved = 0 +local nofstored = 0 +local nofinlined = 0 +local nofdelayed = 0 +local nofinjected = 0 +local h_anchors = 0 +local v_anchors = 0 + +local mt1 = { + __index = function(t,location) + local v = { [v_local] = { }, [v_global] = { } } + t[location] = v + return v + end +} + +local mt2 = { + __index = function(stores,category) + categories[#categories+1] = category + local v = { } + setmetatable(v,mt1) + stores[category] = v + return v + end +} + +setmetatable(displaystore,mt2) + +local defaults = { + __index = { + location = v_left, + align = v_normal, -- not used + method = "", + name = "", + threshold = 0, -- .25ex + margin = v_normal, + scope = v_global, + distance = 0, + hoffset = 0, + voffset = 0, + category = v_default, + line = 0, + vstack = 0, + dy = 0, + baseline = false, + inline = false, + leftskip = 0, + rightskip = 0, + option = { } + } +} + +local enablelocal, enableglobal -- forward reference (delayed initialization) + +local function showstore(store,banner,location) + if next(store) then + for i, si in table.sortedpairs(store) do + local si =store[i] + report_margindata("%s: stored in %a at %s: %a => %s",banner,location,i,validstring(si.name,"no name"),nodes.toutf(getlist(si.box))) + end + else + report_margindata("%s: nothing stored in location %a",banner,location) + end +end + +function margins.save(t) + setmetatable(t,defaults) + local content = takebox(t.number) + local location = t.location + local category = t.category + local inline = t.inline + local scope = t.scope + local name = t.name + local option = t.option + local stack = t.stack + if option then + option = settings_to_hash(option) + t.option = option + end + if not content then + report_margindata("ignoring empty margin data %a",location or "unknown") + return + end + setprop(content,"specialcontent","margindata") + local store + if inline then + store = inlinestore + else + store = displaystore[category][location] + if not store then + report_margindata("invalid location %a",location) + return + end + store = store[scope] + end + if not store then + report_margindata("invalid scope %a",scope) + return + end + if enablelocal and scope == v_local then + enablelocal() + if enableglobal then + enableglobal() -- is the fallback + end + elseif enableglobal and scope == v_global then + enableglobal() + end + nofsaved = nofsaved + 1 + nofstored = nofstored + 1 + if trace_marginstack then + showstore(store,"before",location) + end + if name and name ~= "" then + -- this can be used to overload + if inlinestore then -- todo: inline store has to be done differently (not sparse) + local t = sortedkeys(store) for j=#t,1,-1 do local i = t[j] + local si = store[i] + if si.name == name then + local s = remove(store,i) + flush_node_list(s.box) + end + end + else + for i=#store,1,-1 do + local si = store[i] + if si.name == name then + local s = remove(store,i) + flush_node_list(s.box) + end + end + end + if trace_marginstack then + showstore(store,"between",location) + end + end + if t.number then + local leftmargindistance = texgetdimen("naturalleftmargindistance") + local rightmargindistance = texgetdimen("naturalrightmargindistance") + local strutbox = getbox("strutbox") + local _, strutht, strutdp = getwhd(strutbox) + -- better make a new table and make t entry in t + t.box = content + t.n = nofsaved + -- used later (we will clean up this natural mess later) + -- nice is to make a special status table mechanism + t.strutheight = strutht + t.strutdepth = strutdp + -- beware: can be different from the applied one (we're not in forgetall) + t.leftskip = texget("leftskip",false) + t.rightskip = texget("rightskip",false) + -- + t.leftmargindistance = leftmargindistance -- todo:layoutstatus table + t.rightmargindistance = rightmargindistance + t.leftedgedistance = texgetdimen("naturalleftedgedistance") + + texgetdimen("leftmarginwidth") + + leftmargindistance + t.rightedgedistance = texgetdimen("naturalrightedgedistance") + + texgetdimen("rightmarginwidth") + + rightmargindistance + t.lineheight = texgetdimen("lineheight") + -- + -- t.realpageno = texgetcount("realpageno") + if inline then + local n = new_usernode(inline_mark,nofsaved) + setattrlist(n,true) + context(tonode(n)) -- or use a normal node + store[nofsaved] = t -- no insert + nofinlined = nofinlined + 1 + else + insert(store,t) + end + end + if trace_marginstack then + showstore(store,"after",location) + end + if trace_margindata then + report_margindata("saved %a, location %a, scope %a, inline %a",nofsaved,location,scope,inline) + end +end + +-- Actually it's an advantage to have them all anchored left (tags and such) +-- we could keep them in store and flush in stage two but we might want to +-- do more before that so we need the content to be there unless we can be +-- sure that we flush this first which might not be the case in the future. +-- +-- When the prototype inner/outer code that was part of this proved to be +-- okay it was moved elsewhere. + +local function realign(current,candidate) + local location = candidate.location + local margin = candidate.margin + local hoffset = candidate.hoffset + local distance = candidate.distance + local hsize = candidate.hsize + local width = candidate.width + local align = candidate.align + local inline = candidate.inline + local anchor = candidate.anchor + local hook = candidate.hook + local scope = candidate.scope + local option = candidate.option + local reverse = hook.reverse + local atleft = true + local hmove = 0 + local delta = 0 + local leftpage = isleftpage() + local leftdelta = 0 + local rightdelta = 0 + local leftdistance = distance + local rightdistance = distance + -- + if not anchor or anchor == "" then + anchor = v_text -- this has to become more clever: region:0|column:n|column + end + if margin == v_normal then + -- + elseif margin == v_local then + leftdelta = - candidate.leftskip + rightdelta = candidate.rightskip + elseif margin == v_margin then + leftdistance = candidate.leftmargindistance + rightdistance = candidate.rightmargindistance + elseif margin == v_edge then + leftdistance = candidate.leftedgedistance + rightdistance = candidate.rightedgedistance + end + if leftpage then + leftdistance, rightdistance = rightdistance, leftdistance + end + if location == v_right then + atleft = false + elseif location == v_inner then + if leftpage then + atleft = false + end + elseif location == v_outer then + if not leftpage then + atleft = false + end + else + -- v_left + end + + local islocal = scope == v_local + local area = (not islocal or option[v_text]) and anchor or nil + + if atleft then + delta = hoffset + leftdelta + leftdistance + else + delta = hoffset + rightdelta + rightdistance + end + + local delta, hmove = calculatedelta ( + hook, -- the line + width, -- width of object + delta, -- offset + atleft, + islocal, -- islocal + option[v_paragraph], -- followshape + area -- relative to area + ) + + if hmove ~= 0 then + delta = delta + hmove + if trace_margindata then + report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,hmove) + end + else + if trace_margindata then + report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin) + end + end + moveinline(hook,candidate.node,delta) +end + +local function realigned(current,candidate) + realign(current,candidate) + nofdelayed = nofdelayed - 1 + setprop(current,"margindata",false) + return true +end + +-- Stacking is done in two ways: the v_yes option stacks per paragraph (or line, +-- depending on what gets by) and mostly concerns margin data dat got set at more or +-- less the same time. The v_continue option uses position tracking and works on +-- larger range. However, crossing pages is not part of it. Anyway, when you have +-- such messed up margin data you'd better think twice. +-- +-- The stacked table keeps track (per location) of the offsets (the v_yes case). This +-- table gets saved when the v_continue case is active. We use a special variant +-- of position tracking, after all we only need the page number and vertical position. + +local validstacknames = { + [v_left ] = v_left , + [v_right] = v_right, + [v_inner] = v_inner, + [v_outer] = v_outer, +} + +local cache = { } +local stacked = { [v_yes] = { }, [v_continue] = { } } +local anchors = { [v_yes] = { }, [v_continue] = { } } + +local function resetstacked(all) + stacked[v_yes] = { } + anchors[v_yes] = { } + if all then + stacked[v_continue] = { } + anchors[v_continue] = { } + end +end + +-- anchors are only set for lines that have a note + +local function sa(specification) -- maybe l/r keys ipv left/right keys + local tag = specification.tag + local p = cache[tag] + if p then + if trace_marginstack then + report_margindata("updating anchor %a",tag) + end + p.p = true + p.y = true + -- maybe settobesaved first + setposition("md:v",tag,p) + cache[tag] = nil -- do this later, per page a cleanup + end +end + +local function setanchor(v_anchor) -- freezes the global here + return latelua { action = sa, tag = v_anchor } +end + +local function aa(specification) -- maybe l/r keys ipv left/right keys + local tag = specification.tag + local n = specification.n + local p = jobpositions.gettobesaved('md:v',tag) + if p then + if trace_marginstack then + report_margindata("updating injected %a",tag) + end + local pages = p.pages + if not pages then + pages = { } + p.pages = pages + end + pages[n] = texgetcount("realpageno") + elseif trace_marginstack then + report_margindata("not updating injected %a",tag) + end +end + +local function addtoanchor(v_anchor,n) -- freezes the global here + return latelua { action = aa, tag = v_anchor, n = n } +end + +local function markovershoot(current) -- todo: alleen als offset > line + v_anchors = v_anchors + 1 + cache[v_anchors] = fastcopy(stacked) + local anchor = setanchor(v_anchors) + -- local list = hpack_nodes(setlink(anchor,getlist(current))) -- not ok, we need to retain width + -- local list = setlink(anchor,getlist(current)) -- why not this ... better play safe + local list = hpack_nodes(setlink(anchor,getlist(current)),getwidth(current),"exactly")-- + if trace_marginstack then + report_margindata("marking anchor %a",v_anchors) + end + setlist(current,list) +end + +local function inject(parent,head,candidate) + local box = candidate.box + if not box then + return head, nil, false -- we can have empty texts + end + local width, height, depth + = getwhd(box) + local shift = getshift(box) + local stack = candidate.stack + local stackname = candidate.stackname + local location = candidate.location + local method = candidate.method + local voffset = candidate.voffset + local line = candidate.line + local baseline = candidate.baseline + local strutheight = candidate.strutheight + local strutdepth = candidate.strutdepth + local inline = candidate.inline + local psubtype = getsubtype(parent) + -- This stackname is experimental and therefore undocumented and basically + -- unsupported. It was introduced when we needed to support overlapping + -- of different anchors. + if not stackname or stackname == "" then + stackname = location + else + stackname = validstacknames[stackname] or location + end + local isstacked = stack == v_continue or stack == v_yes + local offset = isstacked and stacked[stack][stackname] + local firstonstack = offset == false or offset == nil + nofinjected = nofinjected + 1 + nofdelayed = nofdelayed + 1 + -- yet untested + baseline = tonumber(baseline) + if not baseline then + baseline = toboolean(baseline) + end + -- + if baseline == true then + baseline = false + else + baseline = tonumber(baseline) + if not baseline or baseline <= 0 then + -- in case we have a box of width 0 that is not analyzed + baseline = false -- strutheight -- actually a hack + end + end + candidate.width = width + candidate.hsize = getwidth(parent) -- we can also pass textwidth + candidate.psubtype = psubtype + candidate.stackname = stackname + if trace_margindata then + report_margindata("processing, index %s, height %p, depth %p, parent %a, method %a",candidate.n,height,depth,listcodes[psubtype],method) + end + -- Overlap detection is somewhat complex because we have display and inline + -- notes mixed as well as inner and outer positioning. We do need to + -- handle it in the stream because we also keep lines together so we keep + -- track of page numbers of notes. + + if isstacked then + firstonstack = true + local anchor = getposition("md:v") + if anchor and (location == v_inner or location == v_outer) then + local pages = anchor.pages + if pages then + local page = pages[nofinjected] + if page then + if isleftpage(page) then + stackname = location == v_inner and v_right or v_left + else + stackname = location == v_inner and v_left or v_right + end + candidate.stackname = stackname + offset = stack and stack ~= "" and stacked[stack][stackname] + end + end + end + local current = v_anchors + 1 + local previous = anchors[stack][stackname] + if trace_margindata then + report_margindata("anchor %i, offset so far %p",current,offset or 0) + end + local ap = anchor and anchor[previous] + local ac = anchor and anchor[current] + if not previous then + elseif previous == current then + firstonstack = false + elseif ap and ac and ap.p == ac.p then + local distance = (ap.y or 0) - (ac.y or 0) + if trace_margindata then + report_margindata("distance %p",distance) + end + if offset > distance then + -- we already overflow + offset = offset - distance + firstonstack = false + else + offset = 0 + end + else + -- what to do + end + anchors[v_yes] [stackname] = current + anchors[v_continue][stackname] = current + if firstonstack then + offset = 0 + end + offset = offset + candidate.dy -- always + shift = shift + offset + else + if firstonstack then + offset = 0 + end + offset = offset + candidate.dy -- always + shift = shift + offset + end + -- Maybe we also need to patch offset when we apply methods, but how ... + -- This needs a bit of playing as it depends on the stack setting of the + -- following which we don't know yet ... so, consider stacking partially + -- experimental. + if method == v_top then + local delta = height - getheight(parent) + if trace_margindata then + report_margindata("top aligned by %p",delta) + end + if delta < candidate.threshold then -- often we need a negative threshold here + shift = shift + voffset + delta + end + elseif method == v_line then + local _, ph, pd = getwhd(parent) + if pd == 0 then + local delta = height - ph + if trace_margindata then + report_margindata("top aligned by %p (no depth)",delta) + end + if delta < candidate.threshold then -- often we need a negative threshold here + shift = shift + voffset + delta + end + end + elseif method == v_first then + if baseline then + shift = shift + voffset + height - baseline -- option + else + shift = shift + voffset -- normal + end + if trace_margindata then + report_margindata("first aligned") + end + elseif method == v_depth then + local delta = strutdepth + if trace_margindata then + report_margindata("depth aligned by %p",delta) + end + shift = shift + voffset + delta + elseif method == v_height then + local delta = - strutheight + if trace_margindata then + report_margindata("height aligned by %p",delta) + end + shift = shift + voffset + delta + elseif voffset ~= 0 then + if trace_margindata then + report_margindata("voffset %p applied",voffset) + end + shift = shift + voffset + end + -- -- -- + if line ~= 0 then + local delta = line * candidate.lineheight + if trace_margindata then + report_margindata("offset %p applied to line %s",delta,line) + end + shift = shift + delta + offset = offset + delta + end + setshift(box,shift) + setwidth(box,0) -- not needed when wrapped + -- + if isstacked then + setlink(box,addtoanchor(v_anchors,nofinjected)) + box = new_hlist(box) + -- set height / depth ? + end + -- + candidate.hook, candidate.node = addtoline(parent,box) + -- + setprop(box,"margindata",candidate) + if trace_margindata then + report_margindata("injected, location %a, stack %a, shift %p",location,stackname,shift) + end + -- we need to add line etc to offset as well + offset = offset + depth + local room = { + height = height, + depth = offset, + slack = candidate.bottomspace, -- todo: 'depth' => strutdepth + lineheight = candidate.lineheight, -- only for tracing + stacked = inline and isstacked, + } + offset = offset + height + -- we need a restart ... when there is no overlap at all + stacked[v_yes] [stackname] = offset + stacked[v_continue][stackname] = offset + -- todo: if no real depth then zero + if trace_margindata then + report_margindata("status, offset %s",offset) + end + return getlist(parent), room, inline and isstacked or (stack == v_continue) +end + +local function flushinline(parent,head) + local current = head + local done = false + local continue = false + local room, don, con, list + while current and nofinlined > 0 do + local id = getid(current) + if id == whatsit_code then + if getsubtype(current) == userdefined_code and getprop(current,"id") == inline_mark then + local n = getprop(current,"data") + local candidate = inlinestore[n] + if candidate then -- no vpack, as we want to realign + inlinestore[n] = nil + nofinlined = nofinlined - 1 + head, room, con = inject(parent,head,candidate) -- maybe return applied offset + done = true + continue = continue or con + nofstored = nofstored - 1 + if room and room.stacked then + -- for now we also check for inline+yes/continue, maybe someday no such check + -- will happen; we can assume most inlines are one line heigh; also this + -- together feature can become optional + registertogether(parent,room) + end + end + end + elseif id == hlist_code or id == vlist_code then + -- optional (but sometimes needed) + list, don, con = flushinline(current,getlist(current)) + setlist(current,list) + continue = continue or con + done = done or don + end + current = getnext(current) + end + return head, done, continue +end + +local function flushed(scope,parent) -- current is hlist + local head = getlist(parent) + local done = false + local continue = false + local room, con, don + for c=1,#categories do + local category = categories[c] + for l=1,#locations do + local location = locations[l] + local store = displaystore[category][location][scope] + if store then + while true do + local candidate = remove(store,1) -- brr, local stores are sparse + if candidate then -- no vpack, as we want to realign + head, room, con = inject(parent,head,candidate) + done = true + continue = continue or con + nofstored = nofstored - 1 + if room then + registertogether(parent,room) + end + else + break + end + end + else + -- report_margindata("fatal error: invalid category %a",category or "?") + end + end + end + if nofinlined > 0 then + if done then + setlist(parent,head) + end + head, don, con = flushinline(parent,head) + continue = continue or con + done = done or don + end + if done then + local a = getattr(head,a_linenumber) -- hack .. we need a more decent critical attribute inheritance mechanism + if false then + local l = hpack_nodes(head,getwidth(parent),"exactly") + setlist(parent,l) + if a then + setattr(l,a_linenumber,a) + end + else + -- because packing messes up profiling + setlist(parent,head) + if a then + setattr(parent,a_linenumber,a) + end + end + end + return done, continue +end + +-- only when group : vbox|vmode_par +-- only when subtype : line, box (no indent alignment cell) + +local function handler(scope,head,group) + if nofstored > 0 then + if trace_margindata then + report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group) + end + local current = head + local done = false -- for tracing only + while current do + local id = getid(current) + if (id == vlist_code or id == hlist_code) and getprop(current,"margindata") == nil then + local don, continue = flushed(scope,current) + if don then + done = true + setprop(current,"margindata",false) -- signal to prevent duplicate processing + if continue then + markovershoot(current) + end + if nofstored <= 0 then + break + end + end + end + current = getnext(current) + end + if trace_margindata then + if done then + report_margindata("flushing stage one, done, %s left",nofstored) + else + report_margindata("flushing stage one, nothing done, %s left",nofstored) + end + end + resetstacked() + end + return head +end + +local trialtypesetting = context.trialtypesetting + +-- maybe change this to an action applied to the to be shipped out box (that is +-- the mvl list in there so that we don't need to traverse global + +function margins.localhandler(head,group) -- sometimes group is "" which is weird + + if trialtypesetting() then + return head + end + + local inhibit = conditionals.inhibitmargindata + if inhibit then + if trace_margingroup then + report_margindata("ignored 3, group %a, stored %s, inhibit %a",group,nofstored,inhibit) + end + return head + end + if nofstored > 0 then + return handler(v_local,head,group) + end + if trace_margingroup then + report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit) + end + return head +end + +function margins.globalhandler(head,group) -- check group + + if trialtypesetting() then + return head, false + end + + local inhibit = conditionals.inhibitmargindata + if inhibit or nofstored == 0 then + if trace_margingroup then + report_margindata("ignored 1, group %a, stored %s, inhibit %a",group,nofstored,inhibit) + end + return head + elseif group == "hmode_par" then + return handler(v_global,head,group) + elseif group == "vmode_par" then -- experiment (for alignments) + return handler(v_global,head,group) + -- this needs checking as we then get quite some one liners to process and + -- we cannot look ahead then: + elseif group == "box" then -- experiment (for alignments) + return handler(v_global,head,group) + elseif group == "alignment" then -- experiment (for alignments) + return handler(v_global,head,group) + else + if trace_margingroup then + report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit) + end + return head + end +end + +local function finalhandler(head) + if nofdelayed > 0 then + local current = head + while current and nofdelayed > 0 do -- traverse_list + local id = getid(current) + if id == hlist_code then -- only lines? + local a = getprop(current,"margindata") + if not a then + finalhandler(getlist(current)) + elseif realigned(current,a) then + if nofdelayed == 0 then + return head, true + end + end + elseif id == vlist_code then + finalhandler(getlist(current)) + end + current = getnext(current) + end + end + return head +end + +function margins.finalhandler(head) + if nofdelayed > 0 then + if trace_margindata then + report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed) + end + head = finalhandler(head) + resetstacked(nofdelayed==0) + else + resetstacked() + end + return head +end + +-- Somehow the vbox builder (in combinations) gets pretty confused and decides to +-- go horizontal. So this needs more testing. + +enablelocal = function() + enableaction("finalizers", "typesetters.margins.localhandler") + enableaction("shipouts", "typesetters.margins.finalhandler") + enablelocal = nil +end + +enableglobal = function() + enableaction("mvlbuilders", "typesetters.margins.globalhandler") + enableaction("shipouts", "typesetters.margins.finalhandler") + enableglobal = nil +end + +statistics.register("margin data", function() + if nofsaved > 0 then + return format("%s entries, %s pending",nofsaved,nofdelayed) + else + return nil + end +end) + +interfaces.implement { + name = "savemargindata", + actions = margins.save, + arguments = { + { + { "location" }, + { "method" }, + { "category" }, + { "name" }, + { "scope" }, + { "number", "integer" }, + { "margin" }, + { "distance", "dimen" }, + { "hoffset", "dimen" }, + { "voffset", "dimen" }, + { "dy", "dimen" }, + { "bottomspace", "dimen" }, + { "baseline"}, -- dimen or string or + { "threshold", "dimen" }, + { "inline", "boolean" }, + { "anchor" }, + -- { "leftskip", "dimen" }, + -- { "rightskip", "dimen" }, + { "align" }, + { "option" }, + { "line", "integer" }, + { "index", "integer" }, + { "stackname" }, + { "stack" }, + } + } +} diff --git a/tex/context/base/mkxl/typo-mar.mkxl b/tex/context/base/mkxl/typo-mar.mkxl index 3b9b99a28..946199f87 100644 --- a/tex/context/base/mkxl/typo-mar.mkxl +++ b/tex/context/base/mkxl/typo-mar.mkxl @@ -34,7 +34,7 @@ %D twice as fast on simple entries. Also, we no longer need an extra pass to get %D inner and outer alignments in sync with the pagebuilder. -\registerctxluafile{typo-mar}{} +\registerctxluafile{typo-mar}{autosuffix} %definesystemattribute[margindata] % only at the lua end diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 23171b9bf..b94ad3846 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-01 17:48 +-- merge date : 2020-12-03 18:56 do -- begin closure to overcome local limits and interference |