if not modules then modules = { } end modules ['attr-ini'] = {
version = 1.001,
comment = "companion to attr-ini.tex",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- todo: document this
-- nb: attributes: color etc is much slower than normal (marks + literals) but ...
-- nb. too many "0 g"s
--
-- nodes
--
nodes = nodes or { }
local format, concat, texsprint = string.format, table.concat, tex.sprint
-- This is not the most ideal place, but it will do. Maybe we need to move
-- attributes to node-att.lua.
do
-- just for testing
local reserved = { }
function nodes.register(n)
reserved[#reserved+1] = n
end
function nodes.cleanup_reserved(nofboxes) -- todo
local nr, free = #reserved, node.free
for i=1,nr do
free(reserved[i])
end
local nl, tb, flush = 0, tex.box, node.flush_list
if nofboxes then
for i=1,nofboxes do
local l = tb[i]
if l then
free(tb[i])
nl = nl + 1
end
end
end
reserved = { }
return nr, nl, nofboxes
end
end
do
local pdfliteral = node.new("whatsit",8) pdfliteral.next, pdfliteral.prev = nil, nil pdfliteral.mode = 1
local disc = node.new("disc") disc.next, disc.prev = nil, nil
local kern = node.new("kern",1) kern.next, kern.prev = nil, nil
local penalty = node.new("penalty") penalty.next, penalty.prev = nil, nil
local glue = node.new("glue") glue.next, glue.prev = nil, nil
local glue_spec = node.new("glue_spec")
local glyph = node.new("glyph",0) glyph.next, glyph.prev = nil, nil
local textdir = node.new("whatsit",7) textdir.next, textdir.prev = nil, nil
nodes.register(pdfliteral)
nodes.register(disc)
nodes.register(kern)
nodes.register(penalty)
nodes.register(glue)
nodes.register(glue_spec)
nodes.register(glyph)
nodes.register(textdir)
local copy = node.copy
function nodes.glyph(fnt,chr)
local n = copy(glyph)
if fnt then n.font = fnt end
if chr then n.char = chr end
return n
end
function nodes.penalty(p)
local n = copy(penalty)
n.penalty = p
return n
end
function nodes.kern(k)
local n = copy(kern)
n.kern = k
return n
end
function nodes.glue(width,stretch,shrink)
local n = copy(glue)
local s = copy(glue_spec)
s.width, s.stretch, s.shrink = width, stretch, shrink
n.spec = s
return n
end
function nodes.glue_spec(width,stretch,shrink)
local s = copy(glue_spec)
s.width, s.stretch, s.shrink = width, stretch, shrink
return s
end
function nodes.disc()
return copy(disc)
end
function nodes.pdfliteral(str)
local t = copy(pdfliteral)
t.data = str
return t
end
function nodes.textdir(dir)
local t = copy(textdir)
t.dir = dir
return t
end
end
function tex.node_mem_status()
-- todo: lpeg
local s = status.node_mem_usage
local t = { }
for n, tag in s:gmatch("(%d+) ([a-z_]+)") do
t[tag] = n
end
return t
end
--
-- attributes
--
attributes = attributes or { }
attributes.names = attributes.names or { }
attributes.numbers = attributes.numbers or { }
attributes.list = attributes.list or { }
input.storage.register(false, "attributes/names", attributes.names, "attributes.names")
input.storage.register(false, "attributes/numbers", attributes.numbers, "attributes.numbers")
input.storage.register(false, "attributes/list", attributes.list, "attributes.list")
function attributes.define(name,number)
attributes.numbers[name], attributes.names[number], attributes.list[number] = number, name, { }
end
-- We can distinguish between rules and glyphs but it's not worth the trouble. A
-- first implementation did that and while it saves a bit for glyphs and rules, it
-- costs more resourses for transparencies. So why bother.
-- namespace for all those features / plural becomes singular
-- i will do the resource stuff later, when we have an interface to pdf (ok, i can
-- fake it with tokens but it will take some coding
function totokens(str)
local t = { }
--~ for c in string.bytes(str) do
for c in str:bytes() do
t[#t+1] = { 12, c }
end
return t
end
-- temp hack, will be proper driver stuff
backends = backends or { }
backends.pdf = backends.pdf or { }
backend = backend or backends.pdf
do
local pdfliteral, register = nodes.pdfliteral, nodes.register
function backends.pdf.literal(str)
local t = pdfliteral(str)
register(t)
return t
end
end
-- shipouts
shipouts = shipouts or { }
do
local pairs = pairs -- in theory faster
local hlist, vlist = node.id('hlist'), node.id('vlist')
local has_attribute = node.has_attribute
nodes.trigger = nodes.trigger or false
nodes.triggering = nodes.triggering or false
-- we used to do the main processor loop here and call processor for each node
-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
-- so that we moved looping to the processor itself; this may lead to a bit of
-- duplicate code once that we have more state handlers
local starttiming, stoptiming = input.starttiming, input.stoptiming
local trigger, numbers = nodes.trigger, attributes.numbers
local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
starttiming(attributes)
local done, used, ok = false, nil, false
local name = plugin.name
local attribute = numbers[name]
local namespace = plugin.namespace
if namespace.enabled then
local processor = plugin.processor
if processor then
local initializer = plugin.initializer
local resolver = plugin.resolver
local inheritance = (resolver and resolver()) or -1
if initializer then
initializer(namespace,attribute,head)
end
head, ok = processor(namespace,attribute,head,inheritance)
if ok then
local finalizer = plugin.finalizer
if finalizer then
head, ok, used = finalizer(namespace,attribute,head)
if used then
local flusher = plugin.flusher
if flusher then
local h, d = flusher(namespace,attribute,head,used)
head = h
end
end
end
done = true
end
end
end
stoptiming(attributes)
return head, done
end
nodes.process_attribute = process_attribute
function nodes.install_attribute_handler(plugin)
return function(head)
return process_attribute(head,plugin)
end
end
end
--
-- generic handlers
--
states = { }
do
local glyph, glue, rule, whatsit, hlist, vlist = node.id('glyph'), node.id('glue'), node.id('rule'), node.id('whatsit'), node.id('hlist'), node.id('vlist')
local has_attribute, copy = node.has_attribute, node.copy
local current, current_selector, used, done = 0, 0, { }, false
function states.initialize(what, attribute, stack)
current, current_selector, used, done = 0, 0, { }, false
end
local function insert(n,stack,previous,head) -- there is a helper, we need previous because we are not slided
if n then
if type(n) == "function" then
n = n()
end
n = copy(n)
n.next = stack
if previous then
previous.next = n
else
head = n
end
previous = n
end
return stack, head
end
function states.finalize(namespace,attribute,head) -- is this one ok?
if current > 0 then
local nn = namespace.none
if nn then
local id = head.id
if id == hlist or id == vlist then
local list = head.list
if list then
local _, h = insert(nn,list,nil,list)
head.list = h
end
else
stack, head = insert(nn,head,nil,head)
end
return head, true, true
end
end
return head, false, false
end
local function process(namespace,attribute,head,inheritance,default) -- one attribute
local trigger = namespace.triggering and nodes.triggering and nodes.trigger
local stack, previous, done = head, nil, false
local nsdata, nsreviver, nsnone = namespace.data, namespace.reviver, namespace.none
while stack do
local id = stack.id
-- if id == glyph or (id == whatsit and stack.subtype == 8) or id == rule or (id == glue and stack.leader) then -- or disc
if id == glyph or id == rule or (id == glue and stack.leader) then -- or disc
local c = has_attribute(stack,attribute)
if c then
if default and c == inheritance then
if current ~= default then
local data = nsdata[default] or nsreviver(default)
stack, head = insert(data,stack,previous,head)
current, done, used[default] = default, true, true
end
elseif current ~= c then
local data = nsdata[c] or nsreviver(c)
stack, head = insert(data,stack,previous,head)
current, done, used[c] = c, true, true
end
-- here ? compare selective
if id == glue then --leader
-- same as *list
local content = stack.leader
if content then
local ok = false
if trigger and has_attribute(stack,trigger) then
local outer = has_attribute(stack,attribute)
if outer ~= inheritance then
stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
else
stack.leader, ok = process(namespace,attribute,content,inheritance,default)
end
else
stack.leader, ok = process(namespace,attribute,content,inheritance,default)
end
done = done or ok
end
end
elseif default and inheritance then
if current ~= default then
local data = nsdata[default] or nsreviver(default)
stack, head = insert(data,stack,previous,head)
current, done, used[default] = default, true, true
end
elseif current > 0 then
stack, head = insert(nsnone,stack,previous,head)
current, done, used[0] = 0, true, true
end
elseif id == hlist or id == vlist then
local content = stack.list
if content then
local ok = false
if trigger and has_attribute(stack,trigger) then
local outer = has_attribute(stack,attribute)
if outer ~= inheritance then
stack.list, ok = process(namespace,attribute,content,inheritance,outer)
else
stack.list, ok = process(namespace,attribute,content,inheritance,default)
end
else
stack.list, ok = process(namespace,attribute,content,inheritance,default)
end
done = done or ok
end
end
previous = stack
stack = stack.next
end
-- we need to play safe
if current > 0 then
stack, head = insert(nsnone,stack,previous,head)
current, current_selector, done, used[0] = 0, 0, true, true
end
return head, done
end
states.process = process
-- we can force a selector, e.g. document wide color spaces, saves a little
-- watch out, we need to check both the selector state (like colorspace) and
-- the main state (like color), otherwise we get into troubles when a selector
-- state changes while the main state stays the same (like two glyphs following
-- each other with the same color but different color spaces e.g. \showcolor)
local function selective(namespace,attribute,head,inheritance,default) -- two attributes
local trigger = namespace.triggering and nodes.triggering and nodes.trigger
local stack, previous, done = head, nil, false
-- local nsselector, nsforced, nsselector = namespace.default, namespace.forced, namespace.selector
local nsforced, nsselector = namespace.forced, namespace.selector
local nsdata, nsreviver, nsnone = namespace.data, namespace.reviver, namespace.none
while stack do
local id = stack.id
-- if id == glyph or (id == whatsit and stack.subtype == 8) or id == rule or (id == glue and stack.leader) then -- or disc
if id == glyph or id == rule or (id == glue and stack.leader) then -- or disc
local c = has_attribute(stack,attribute)
if c then
if default and c == inheritance then
if current ~= default then
local data = nsdata[default] or nsreviver(default)
stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head)
current, done, used[default] = default, true, true
end
else
local s = has_attribute(stack,nsselector)
if current ~= c or current_selector ~= s then
local data = nsdata[c] or nsreviver(c)
stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head)
current, current_selector, done, used[c] = c, s, true, true
end
end
elseif default and inheritance then
if current ~= default then
local data = nsdata[default] or nsreviver(default)
stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head)
current, done, used[default] = default, true, true
end
elseif current > 0 then
stack, head = insert(nsnone,stack,previous,head)
current, current_selector, done, used[0] = 0, 0, true, true
end
if id == glue then -- leader
-- same as *list
local content = stack.leader
if content then
local ok = false
if trigger and has_attribute(stack,trigger) then
local outer = has_attribute(stack,attribute)
if outer ~= inheritance then
stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
else
stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
end
else
stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
end
done = done or ok
end
end
elseif id == hlist or id == vlist then
local content = stack.list
if content then
local ok = false
if trigger and has_attribute(stack,trigger) then
local outer = has_attribute(stack,attribute)
if outer ~= inheritance then
stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
else
stack.list, ok = selective(namespace,attribute,content,inheritance,default)
end
else
stack.list, ok = selective(namespace,attribute,content,inheritance,default)
end
done = done or ok
end
end
previous = stack
stack = stack.next
end
-- we need to play safe
if current > 0 then
stack, head = insert(nsnone,stack,previous,head)
current, current_selector, done, used[0] = 0, 0, true, true
end
return head, done
end
states.selective = selective
end
states = states or { }
states.collected = states.collected or { }
input.storage.register(false,"states/collected", states.collected, "states.collected")
function states.collect(str)
local collected = states.collected
collected[#collected+1] = str
end
function states.flush()
local collected = states.collected
if #collected > 0 then
for i=1,#collected do
texsprint(tex.ctxcatcodes,collected[i]) -- we're in context mode anyway
end
states.collected = { }
end
end
function states.check()
texio.write_nl(concat(states.collected,"\n"))
end
--
-- colors
--
-- we can also collapse the two attributes: n, n+1, n+2 and then
-- at the tex end add 0, 1, 2, but this is not faster and less
-- flexible (since sometimes we freeze color attribute values at
-- the lua end of the game
-- we also need to store the colorvalues because we need then in mp
colors = colors or { }
colors.data = colors.data or { }
colors.values = colors.values or { }
colors.registered = colors.registered or { }
colors.enabled = true
colors.weightgray = true
colors.attribute = 0
colors.selector = 0
colors.default = 1
colors.main = nil
colors.triggering = true
-- This is a compromis between speed and simplicity. We used to store the
-- values and data in one array, which made in neccessary to store the
-- converters that need node constructor into strings and evaluate them
-- at runtime (after reading from storage). Think of:
--
-- colors.strings = colors.strings or { }
--
-- if environment.initex then
-- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")"
-- end
--
-- input.storage.register(true,"colors/data", colors.strings, "colors.data") -- evaluated
--
-- We assume that only processcolors are defined in the format.
input.storage.register(false,"colors/values", colors.values, "colors.values")
input.storage.register(false,"colors/registered", colors.registered, "colors.registered")
colors.stamps = {
rgb = "r:%s:%s:%s",
cmyk = "c:%s:%s:%s:%s",
gray = "s:%s",
spot = "p:%s:%s:%s:%s"
}
colors.models = {
all = 1,
gray = 2,
rgb = 3,
cmyk = 4,
}
colors.model = "all"
do
local min = math.min
local max = math.max
local function rgbdata(r,g,b) -- dodo: backends.pdf.rgbdata
return backends.pdf.literal(format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b))
end
local function cmykdata(c,m,y,k)
return backends.pdf.literal(format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k))
end
local function graydata(s)
return backends.pdf.literal(format("%s g %s G",s,s))
end
local function spotdata(n,f,d,p)
if type(p) == "string" then
p = p:gsub(","," ") -- brr misuse of spot
end
return backends.pdf.literal(format("/%s cs /%s CS %s SCN %s scn",n,n,p,p))
end
local function rgbtocmyk(r,g,b) -- we could reduce
return 1-r, 1-g, 1-b, 0
end
local function cmyktorgb(c,m,y,k)
return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
end
local function rgbtogray(r,g,b)
if colors.weightgray then
return .30*r+.59*g+.11*b
else
return r/3+g/3+b/3
end
end
local function cmyktogray(c,m,y,k)
return rgbtogray(cmyktorgb(c,m,y,k))
end
colors.rgbtocmyk = rgbtocmyk
colors.rgbtogray = rgbtogray
colors.cmyktorgb = cmyktorgb
colors.cmyktogray = cmyktogray
-- we can share some *data by using s, rgb and cmyk hashes, but
-- normally the amount of colors is not that large; storing the
-- components costs a bit of extra runtime, but we expect to gain
-- some back because we have them at hand; the number indicates the
-- default color space
function colors.gray(s)
return { 2, s, s, s, s, 0, 0, 0, 1-s }
end
function colors.rgb(r,g,b)
local s = rgbtogray(r,g,b)
local c, m, y, k = rgbtocmyk(r,g,b)
return { 3, s, r, g, b, c, m, y, k }
end
function colors.cmyk(c,m,y,k)
local s = cmyktogray(c,m,y,k)
local r, g, b = cmyktorgb(c,m,y,k)
return { 4, s, r, g, b, c, m, y, k }
end
--~ function colors.spot(parent,f,d,p)
--~ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
--~ end
function colors.spot(parent,f,d,p)
if type(p) == "number" then
local n = attributes.list[attributes.numbers.color][parent] -- hard coded ref to color number
if n then
local v = colors.values[n]
if v then
-- the via cmyk hack is dirty, but it scales better
local c, m, y, k = p*v[6], p*v[7], p*v[8], p*v[8]
local r, g, b = cmyktorgb(c,m,y,k)
local s = cmyktogray(c,m,y,k)
return { 5, s, r, g, b, c, m, y, k, parent, f, d, p }
end
end
else
-- todo, multitone (maybe p should be a table)
end
return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
end
function colors.reviver(n)
local d = colors.data[n]
if not d then
local v = colors.values[n]
if not v then
local gray = graydata(0)
d = { gray, gray, gray, gray }
logs.report("attributes","unable to revive color %s",n or "?")
else
local kind, gray, rgb, cmyk = v[1], graydata(v[2]), rgbdata(v[3],v[4],v[5]), cmykdata(v[6],v[7],v[8],v[9])
if kind == 2 then
d = { gray, gray, gray, gray }
elseif kind == 3 then
d = { rgb, gray, rgb, cmyk }
elseif kind == 4 then
d = { cmyk, gray, rgb, cmyk }
elseif kind == 5 then
local spot = spotdata(v[10],v[11],v[12],v[13])
d = { spot, gray, rgb, cmyk }
end
end
colors.data[n] = d
end
return d
end
function colors.filter(n)
return concat(colors.data[n],":",5)
end
colors.none = graydata(0)
end
function colors.setmodel(attribute,name)
colors.model = name
colors.selector = attributes.numbers[attribute]
colors.default = colors.models[name] or 1
return colors.default
end
function colors.register(attribute, name, colorspace, ...) -- passing 9 vars is faster
local stamp = format(colors.stamps[colorspace], ...)
local color = colors.registered[stamp]
if not color then
color = #colors.values+1
colors.values[color] = colors[colorspace](...)
colors.registered[stamp] = color
colors.reviver(color)
end
if name then
attributes.list[attributes.numbers[attribute]][name] = color -- not grouped, so only global colors
end
return colors.registered[stamp]
end
function colors.value(id)
return colors.values[id]
end
shipouts.handle_color = nodes.install_attribute_handler {
name = "color",
namespace = colors,
initializer = states.initialize,
finalizer = states.finalize,
processor = states.selective,
resolver = function(...) return colors.main end,
}
-- transparencies
-- for the moment we manage transparencies in the pdf driver because
-- first we need a nice interface to some pdf things
transparencies = transparencies or { }
transparencies.registered = transparencies.registered or { }
transparencies.data = transparencies.data or { }
transparencies.values = transparencies.values or { }
transparencies.enabled = false
transparencies.template = "%s:%s"
transparencies.triggering = true
input.storage.register(false, "transparencies/registered", transparencies.registered, "transparencies.registered")
input.storage.register(false, "transparencies/values", transparencies.values, "transparencies.values")
function transparencies.reference(n)
return backends.pdf.literal(format("/Tr%s gs",n))
end
function transparencies.register(name,a,t)
local stamp = format(transparencies.template,a,t)
local n = transparencies.registered[stamp]
if not n then
n = #transparencies.data+1
transparencies.data[n] = transparencies.reference(n)
transparencies.values[n] = { a, t }
transparencies.registered[stamp] = n
states.collect(format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,a,t)) -- too many, but experimental anyway
end
return transparencies.registered[stamp]
end
function transparencies.reviver(n)
local d = transparencies.data[n]
if not d then
local v = transparencies.values[n]
if not v then
d = transparencies.reference(0)
logs.report("attributes","unable to revive transparency %s",n or "?")
else
d = transparencies.reference(n)
states.collect(format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,v[1],v[2]))
end
transparencies.data[n] = d
end
return d
end
-- check if there is an identity
transparencies.none = transparencies.reference(0) -- for the moment the pdf backend does this
function transparencies.value(id)
return transparencies.values[id]
end
shipouts.handle_transparency = nodes.install_attribute_handler {
name = "transparency",
namespace = transparencies,
initializer = states.initialize,
finalizer = states.finalize ,
processor = states.process ,
}
--- overprint / knockout
overprints = overprints or { }
overprints.data = overprints.data or { }
overprints.enabled = false
overprints.data[1] = backends.pdf.literal(format("/GSoverprint gs"))
overprints.data[2] = backends.pdf.literal(format("/GSknockout gs"))
overprints.none = overprints.data[2]
overprints.registered = {
overprint = 1,
knockout = 2,
}
--~ input.storage.register(false, "overprints/registered", overprints.registered, "overprints.registered")
--~ input.storage.register(false, "overprints/data", overprints.data, "overprints.data")
function overprints.register(stamp)
-- states.collect(texsprint(tex.ctxcatcodes,"\\initializePDFoverprint")) -- to be testd
return overprints.registered[stamp] or overprints.registered.overprint
end
shipouts.handle_overprint = nodes.install_attribute_handler {
name = "overprint",
namespace = overprints,
initializer = states.initialize,
finalizer = states.finalize ,
processor = states.process ,
}
--- negative / positive
negatives = negatives or { }
negatives.data = negatives.data or { }
negatives.enabled = false
negatives.data[1] = backends.pdf.literal(format("/GSpositive gs"))
negatives.data[2] = backends.pdf.literal(format("/GSnegative gs"))
negatives.none = negatives.data[1]
negatives.registered = {
positive = 1,
negative = 2,
}
function negatives.register(stamp)
-- states.collect(texsprint(tex.ctxcatcodes,"\\initializePDFnegative")) -- to be testd
return negatives.registered[stamp] or negatives.registered.positive
end
shipouts.handle_negative = nodes.install_attribute_handler {
name = "negative",
namespace = negatives,
initializer = states.initialize,
finalizer = states.finalize,
processor = states.process,
}
-- effects
effects = effects or { }
effects.data = effects.data or { }
effects.registered = effects.registered or { }
effects.enabled = false
effects.stamp = "%s:%s:%s"
input.storage.register(false, "effects/registered", effects.registered, "effects.registered")
input.storage.register(false, "effects/data", effects.data, "effects.data")
function effects.register(effect,stretch,rulethickness)
local stamp = format(effects.stamp,effect,stretch,rulethickness)
local n = effects.registered[stamp]
if not n then
n = #effects.data+1
effects.data[n] = effects.reference(effect,stretch,rulethickness)
effects.registered[stamp] = n
-- states.collect("") -- nothing
end
return effects.registered[stamp]
end
backends.pdf.effects = {
normal = 0,
inner = 0,
outer = 1,
both = 2,
hidden = 3,
}
function effects.reference(effect,stretch,rulethickness)
-- always, no zero test (removed)
rulethickness = number.dimenfactors["bp"]*rulethickness
effect = backends.pdf.effects[effect] or backends.pdf.effects['normal']
return backends.pdf.literal(format("%s Tc %s w %s Tr",stretch,rulethickness,effect)) -- watch order
end
effects.none = effects.reference(0,0,0) -- faster: backends.pdf.literal("0 Tc 0 w 0 Tr")
shipouts.handle_effect = nodes.install_attribute_handler {
name = "effect",
namespace = effects,
initializer = states.initialize,
finalizer = states.finalize,
processor = states.process,
}
-- layers (ugly code, due to no grouping and such)
viewerlayers = viewerlayers or { }
viewerlayers.data = viewerlayers.data or { }
viewerlayers.registered = viewerlayers.registered or { }
viewerlayers.stamp = "%s"
viewerlayers.enabled = false
input.storage.register(false, "viewerlayers/registered", viewerlayers.registered, "viewerlayers.registered")
--~ input.storage.register(false, "viewerlayers/data", viewerlayers.data, "viewerlayers.data")
local somedone = false
local somedata = { }
local nonedata = backends.pdf.literal("EMC")
function viewerlayers.none() -- no local
if somedone then
somedone = false
return nonedata
else
return nil
end
end
local function some(name)
local sd = somedata[name]
if not sd then
sd = {
backends.pdf.literal(format("EMC /OC /%s BDC",name)),
backends.pdf.literal(format( "/OC /%s BDC",name)),
}
somedata[name] = sd
end
if somedone then
return sd[1]
else
somedone = true
return sd[2]
end
end
local function initializer(...)
somedone = false
return states.initialize(...)
end
viewerlayers.register = function(name)
local stamp = format(viewerlayers.stamp,name)
local n = viewerlayers.registered[stamp]
if not n then
n = #viewerlayers.data + 1
viewerlayers.data[n] = function() return some(name) end
viewerlayers.registered[stamp] = n
end
return viewerlayers.registered[stamp]
end
shipouts.handle_viewerlayer = nodes.install_attribute_handler {
name = "viewerlayer",
namespace = viewerlayers,
initializer = initializer,
finalizer = states.finalize,
processor = states.process,
}
--~ nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_viewerlayer", nil, "notail")