summaryrefslogtreecommitdiff
path: root/tex/context/base/mkxl/attr-ini.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkxl/attr-ini.lmt')
-rw-r--r--tex/context/base/mkxl/attr-ini.lmt352
1 files changed, 352 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/attr-ini.lmt b/tex/context/base/mkxl/attr-ini.lmt
new file mode 100644
index 000000000..ca21365cb
--- /dev/null
+++ b/tex/context/base/mkxl/attr-ini.lmt
@@ -0,0 +1,352 @@
+if not modules then modules = { } end modules ['attr-ini'] = {
+ version = 1.001,
+ comment = "companion to attr-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 osexit = os.exit
+local sortedhash = table.sortedhash
+
+--[[ldx--
+<p>We start with a registration system for atributes so that we can use the
+symbolic names later on.</p>
+--ldx]]--
+
+local nodes = nodes
+local context = context
+local storage = storage
+local commands = commands
+
+local implement = interfaces.implement
+
+attributes = attributes or { }
+local attributes = attributes
+
+local sharedstorage = storage.shared
+
+local texsetattribute = tex.setattribute
+
+attributes.names = attributes.names or { }
+attributes.numbers = attributes.numbers or { }
+attributes.list = attributes.list or { }
+attributes.values = attributes.values or { }
+attributes.counts = attributes.counts or { }
+attributes.handlers = attributes.handlers or { }
+attributes.states = attributes.states or { }
+attributes.unsetvalue = -0x7FFFFFFF
+
+local currentfont = font.current
+local currentattributes = nodes and nodes. currentattributes or node.currentattributes
+local getusedattributes = nodes and nodes.nuts and nodes.nuts.getusedattributes or node.direct.getusedattributes
+
+local names = attributes.names
+local numbers = attributes.numbers
+local list = attributes.list
+local values = attributes.values
+local counts = attributes.counts
+
+storage.register("attributes/names", names, "attributes.names")
+storage.register("attributes/numbers", numbers, "attributes.numbers")
+storage.register("attributes/list", list, "attributes.list")
+storage.register("attributes/values", values, "attributes.values")
+storage.register("attributes/counts", counts, "attributes.counts")
+
+local report_attribute = logs.reporter("attributes")
+local report_value = logs.reporter("attributes","values")
+
+local trace_values = false
+
+trackers.register("attributes.values", function(v) trace_values = v end)
+
+-- function attributes.define(name,number) -- at the tex end
+-- if not numbers[name] then
+-- numbers[name] = number
+-- names[number] = name
+-- list[number] = { }
+-- end
+-- end
+
+--[[ldx--
+<p>We reserve this one as we really want it to be always set (faster).</p>
+--ldx]]--
+
+names[0], numbers["fontdynamic"] = "fontdynamic", 0
+
+--[[ldx--
+<p>private attributes are used by the system and public ones are for users. We use dedicated
+ranges of numbers for them. Of course a the <l n='context'/> end a private attribute can be
+accessible too, so a private attribute can have a public appearance.</p>
+--ldx]]--
+
+sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 15 -- very private
+sharedstorage.attributes_last_public = sharedstorage.attributes_last_public or 1024 -- less private
+
+function attributes.private(name) -- at the lua end (hidden from user)
+ local number = numbers[name]
+ if not number then
+ local last = sharedstorage.attributes_last_private
+ if last < 1023 then
+ last = last + 1
+ sharedstorage.attributes_last_private = last
+ else
+ report_attribute("no more room for private attributes")
+ osexit()
+ end
+ number = last
+ numbers[name], names[number], list[number] = number, name, { }
+ end
+ return number
+end
+
+function attributes.public(name) -- at the lua end (hidden from user)
+ local number = numbers[name]
+ if not number then
+ local last = sharedstorage.attributes_last_public
+ if last < 65535 then
+ last = last + 1
+ sharedstorage.attributes_last_public = last
+ else
+ report_attribute("no more room for public attributes")
+ osexit()
+ end
+ number = last
+ numbers[name], names[number], list[number] = number, name, { }
+ end
+ return number
+end
+
+attributes.system = attributes.private
+
+function attributes.define(name,category)
+ return (attributes[category or "public"] or attributes["public"])(name)
+end
+
+-- tracers
+
+local function showlist(what,list)
+ if list then
+ local a = list.next
+ local i = 0
+ while a do
+ local number = a.index
+ local value = a.value
+ i = i + 1
+ report_attribute("%S %2i: attribute %3i, value %4i, name %a",what,i,number,value,names[number])
+ a = a.next
+ end
+ end
+end
+
+function attributes.showcurrent()
+ showlist("current",currentattributes())
+end
+
+function attributes.ofnode(n)
+ showlist(n,n.attr)
+end
+
+-- rather special (can be optimized)
+
+local store = { }
+
+function attributes.save(name)
+ name = name or ""
+ local n = currentattributes()
+ n = n and n.next
+ local t = { }
+ while n do
+ t[n.index] = n.value
+ n = n.next
+ end
+ store[name] = {
+ attr = t,
+ font = currentfont(),
+ }
+end
+
+function attributes.restore(name)
+ name = name or ""
+ local t = store[name]
+ if t then
+ local attr = t.attr
+ local font = t.font
+ if attr then
+ for k, v in next, attr do
+ texsetattribute(k,v)
+ end
+ end
+ if font then
+ -- tex.font = font
+ -- context.getvalue(fonts.hashes.csnames[font])
+ currentfont(font)
+ end
+ end
+ -- store[name] = nil
+end
+
+-- value manager
+
+local cleaners = { }
+
+-- function attributes.registervalue(index,value)
+-- local list = values[index]
+-- local last
+-- if list then
+-- last = counts[index] + 1
+-- list[last] = value
+-- else
+-- last = 1
+-- values[index] = { value }
+-- end
+-- counts[index] = last
+-- return last
+-- end
+
+function attributes.registervalue(index,value)
+ local list = values[index]
+ local last
+ if list then
+ local c = counts[index]
+ if c and c[2] > 0 then
+ -- this can be an option
+ for i=c[1],c[2] do
+ if list[i] == nil then
+ if trace_values then
+ report_value("reusing slot %i for attribute %i in range (%i,%i)",i,index,c[1],c[2])
+ end
+ c[1] = i
+ list[i] = value
+ return i
+ end
+ end
+ else
+ c = { 0, 0 }
+ end
+ last = c[2] + 1
+ list[last] = value
+ c[1] = last
+ c[2] = last
+ if trace_values then
+ report_value("expanding to slot %i for attribute %i",last,index)
+ end
+ else
+ last = 1
+ values[index] = { value }
+ counts[index] = { last, last }
+ if trace_values then
+ report_value("starting at slot %i for attribute %i",last,index)
+ end
+ end
+ return last
+end
+
+function attributes.getvalue(index,value)
+ local list = values[index]
+ return list and list[value] or nil
+end
+
+function attributes.hasvalues(index)
+ local list = values[index]
+ return list and next(list) and true or false
+end
+
+function attributes.setcleaner(index,cleaner)
+ cleaners[index] = cleaner
+end
+
+function attributes.checkvalues()
+-- if true then
+-- report_value("no checking done")
+-- return
+-- end
+ if next(values) then
+ local active = getusedattributes()
+ if trace_values then
+ -- sorted
+ for index, list in sortedhash(values) do
+ local b = active[index]
+ if b then
+ local cleaner = cleaners[index]
+ for k in sortedhash(list) do
+ if b[k] then
+ report_value("keeping value %i for attribute %i",k,index)
+ else
+ report_value("wiping value %i for attribute %i",k,index)
+ if cleaner then
+ cleaner(list[k])
+ end
+ list[k] = nil
+ end
+ end
+ if next(list) then
+ counts[index][1] = 0
+ goto continue
+ end
+ end
+ report_value("no more values for attribute %i",index)
+ values[index] = nil
+ counts[index] = nil
+ ::continue::
+ end
+ else
+ for index, list in next, values do
+ local b = active[index]
+ if b then
+ local cleaner = cleaners[index]
+ for k in next, list do
+ if not b[k] then
+ if cleaner then
+ cleaner(list[k])
+ end
+ list[k] = nil
+ end
+ end
+ if next(list) then
+ counts[index][1] = 0
+ goto continue
+ end
+ end
+ values[index] = nil
+ counts[index] = { 0, 0 }
+ ::continue::
+ end
+ end
+ elseif trace_values then
+ report_value("no check needed")
+ end
+end
+
+implement {
+ name = "cleanupattributes",
+ -- public = true, -- some day ... but then also \shipoutpage
+ protected = true,
+ actions = attributes.checkvalues,
+}
+
+-- interface
+
+implement {
+ name = "defineattribute",
+ arguments = "2 strings",
+ actions = { attributes.define, context }
+}
+
+implement {
+ name = "showattributes",
+ actions = attributes.showcurrent
+}
+
+implement {
+ name = "savecurrentattributes",
+ arguments = "string",
+ actions = attributes.save
+}
+
+implement {
+ name = "restorecurrentattributes",
+ arguments = "string",
+ actions = attributes.restore
+}