summaryrefslogtreecommitdiff
path: root/tex
diff options
context:
space:
mode:
Diffstat (limited to 'tex')
-rw-r--r--tex/context/base/mkii/cont-new.mkii2
-rw-r--r--tex/context/base/mkii/context.mkii2
-rw-r--r--tex/context/base/mkiv/back-exp.lua4
-rw-r--r--tex/context/base/mkiv/back-ini.lua28
-rw-r--r--tex/context/base/mkiv/cont-new.mkiv2
-rw-r--r--tex/context/base/mkiv/context.lus15
-rw-r--r--tex/context/base/mkiv/context.mkiv2
-rw-r--r--tex/context/base/mkiv/driv-ini.lua4
-rw-r--r--tex/context/base/mkiv/lpdf-mis.lua6
-rw-r--r--tex/context/base/mkiv/luat-fmt.lua2
-rw-r--r--tex/context/base/mkiv/status-files.pdfbin26137 -> 26093 bytes
-rw-r--r--tex/context/base/mkiv/status-lua.pdfbin256029 -> 255687 bytes
-rw-r--r--tex/context/base/mkiv/task-ini.lua5
-rw-r--r--tex/context/base/mkiv/type-ini.lua8
-rw-r--r--tex/context/base/mkiv/typo-lin.lua2
-rw-r--r--tex/context/base/mkxl/back-ini.lmt157
-rw-r--r--tex/context/base/mkxl/back-ini.mkxl2
-rw-r--r--tex/context/base/mkxl/back-out.lmt4
-rw-r--r--tex/context/base/mkxl/back-res.lmt177
-rw-r--r--tex/context/base/mkxl/back-res.mkxl2
-rw-r--r--tex/context/base/mkxl/back-trf.lmt164
-rw-r--r--tex/context/base/mkxl/back-trf.mkxl2
-rw-r--r--tex/context/base/mkxl/cont-new.mkxl2
-rw-r--r--tex/context/base/mkxl/context.mkxl2
-rw-r--r--tex/context/base/mkxl/driv-ini.lmt230
-rw-r--r--tex/context/base/mkxl/driv-ini.mkxl2
-rw-r--r--tex/context/base/mkxl/font-lib.mklx2
-rw-r--r--tex/context/base/mkxl/lpdf-ini.lmt7
-rw-r--r--tex/context/base/mkxl/lpdf-lmt.lmt153
-rw-r--r--tex/context/base/mkxl/lpdf-mis.lmt6
-rw-r--r--tex/context/base/mkxl/meta-fig.mkxl4
-rw-r--r--tex/context/base/mkxl/node-fin.mkxl5
-rw-r--r--tex/context/base/mkxl/node-fnt.lmt580
-rw-r--r--tex/context/base/mkxl/node-ini.mkxl4
-rw-r--r--tex/context/base/mkxl/node-res.lmt622
-rw-r--r--tex/context/base/mkxl/node-rul.lmt797
-rw-r--r--tex/context/base/mkxl/node-rul.mkxl2
-rw-r--r--tex/context/base/mkxl/node-scn.lmt314
-rw-r--r--tex/context/base/mkxl/node-shp.lmt115
-rw-r--r--tex/context/base/mkxl/page-imp.mkxl8
-rw-r--r--tex/context/base/mkxl/page-lin.lmt418
-rw-r--r--tex/context/base/mkxl/page-lin.mklx29
-rw-r--r--tex/context/base/mkxl/supp-box.lmt1
-rw-r--r--tex/context/base/mkxl/type-ini.lmt45
-rw-r--r--tex/context/base/mkxl/type-ini.mklx4
-rw-r--r--tex/context/base/mkxl/typo-lin.lmt149
-rw-r--r--tex/context/base/mkxl/typo-mar.lmt995
-rw-r--r--tex/context/base/mkxl/typo-mar.mkxl2
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua2
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
index ea407d533..b6aed6495 100644
--- a/tex/context/base/mkiv/status-files.pdf
+++ b/tex/context/base/mkiv/status-files.pdf
Binary files differ
diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf
index 97cefc1a8..29161ec70 100644
--- a/tex/context/base/mkiv/status-lua.pdf
+++ b/tex/context/base/mkiv/status-lua.pdf
Binary files differ
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