diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/attr-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/attr-ini.lua | 1078 |
1 files changed, 374 insertions, 704 deletions
diff --git a/Master/texmf-dist/tex/context/base/attr-ini.lua b/Master/texmf-dist/tex/context/base/attr-ini.lua index c339d467214..535488c35b1 100644 --- a/Master/texmf-dist/tex/context/base/attr-ini.lua +++ b/Master/texmf-dist/tex/context/base/attr-ini.lua @@ -6,505 +6,29 @@ if not modules then modules = { } end modules ['attr-ini'] = { 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 = string.format, table.concat - --- 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 - -- flush(l) - tb[i] = nil - 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") glue_spec.next, glue_spec.prev = nil, nil - local glyph = node.new("glyph",0) glyph.next, glyph.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) - - 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 - -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 +-- this module is being reconstructed +-- we can also do the nsnone via a metatable and then also se index 0 --- --- attributes --- +local type = type +local format, gmatch = string.format, string.gmatch +local concat = table.concat +local texsprint = tex.sprint -attributes = attributes or { } +local ctxcatcodes = tex.ctxcatcodes -attributes.names = attributes.names or { } -attributes.numbers = attributes.numbers or { } -attributes.list = attributes.list or { } +-- todo: document this -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") +-- nb: attributes: color etc is much slower than normal (marks + literals) but ... +-- nb. too many "0 g"s -function attributes.define(name,number) - attributes.numbers[name], attributes.names[number], attributes.list[number] = number, name, { } -end +nodes = nodes or { } +states = states or { } +shipouts = shipouts or { } -- 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 { } -shipouts.plugins = shipouts.plugins 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 function process_attributes(head,plugins) - if head then -- is already tested - starttiming(attributes) - local done, used = false, { } - local trigger, numbers = nodes.trigger, attributes.numbers - for name, plugin in pairs(plugins) do - local attribute = numbers[name] - if attribute then - local namespace = plugin.namespace - if namespace.enabled then - local initializer = plugin.initializer - local processor = plugin.processor - local finalizer = plugin.finalizer - local resolver = plugin.resolver - if initializer then - initializer(namespace,attribute,head) - end - if processor then - local inheritance = (resolver and resolver()) or -1 - local ok -- = false - head, ok = processor(namespace,attribute,head,inheritance) - done = done or ok - end - if finalizer then -- no need when not ok - local ok -- = false - head, ok, used[attribute] = finalizer(namespace,attribute,head) - done = done or ok - end - end - else - texio.write_nl(format("undefined attribute %s",name)) - end - end - if done then - for name, plugin in pairs(plugins) do - local attribute = numbers[name] - if used[attribute] then - local namespace = plugin.namespace - if namespace.enabled then - local flusher = plugin.flusher - if flusher then - local h, d = flusher(namespace,attribute,head,used[attribute]) - head = h - end - end - end - end - end - stoptiming(attributes) - return head, done - else - return head, false - end - end - - nodes.process_attributes = process_attributes - - --~ glyph = 746876 - --~ glue = 376096 - --~ hlist = 152284 - --~ disc = 47224 - --~ kern = 41504 - --~ penalty = 31964 - --~ whatsit = 29048 - --~ vlist = 20136 - --~ rule = 13292 - --~ mark = 4304 - --~ math = 1072 - - local disc, mark, free = node.id('disc'), node.id('mark'), node.free - - local function cleanup_page(head) -- rough - local prev, start = nil, head - while start do - local id, nx = start.id, start.next - if id == disc or id == mark then - if prev then - prev.next = nx - end - if start == head then - head = nx - end - local tmp = start - start = nx - free(tmp) - elseif id == hlist or id == vlist then - local sl = start.list - if sl then - start.list = cleanup_page(sl) - end - prev, start = start, nx - else - prev, start = start, nx - end - end - return head - end - - nodes.cleanup_page = cleanup_page - - nodes.cleanup_page_first = false - - function nodes.process_page(head) - if nodes.cleanup_page_first then - head = cleanup_page(head) - end - return process_attributes(head,shipouts.plugins) - end - -end - --- --- generic handlers --- - -states = { } - -do - - local glyph, rule, whatsit, hlist, vlist = node.id('glyph'), node.id('rule'), node.id('whatsit'), node.id('hlist'), node.id('vlist') - - local has_attribute, copy = node.has_attribute, node.copy - - local current, used, done = 0, { }, false - - function states.initialize(what, attribute, stack) - current, used, done = 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 - 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 or id == rule 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 - 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 - return head, done - end - - states.process = process - - -- we can force a selector, e.g. document wide color spaces, saves a little - - 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 nsdata, nsreviver, nsnone = namespace.data, namespace.reviver, namespace.none - while stack do - local id = stack.id - if id == glyph or id == whatsit or id == rule then -- or disc - -- todo: maybe track two states, also selector - 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 - elseif current ~= c then - local data = nsdata[c] or nsreviver(c) - stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head) - current, done, used[c] = c, true, true - 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, 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 = 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 - 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 - tex.sprint(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 -- @@ -513,21 +37,9 @@ end -- 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 @@ -539,21 +51,34 @@ colors.triggering = true -- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")" -- end -- --- input.storage.register(true,"colors/data", colors.strings, "colors.data") -- evaluated +-- storage.register("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 = colors or { } +colors.data = colors.data or { } +colors.values = colors.values or { } +colors.registered = colors.registered or { } -colors.stamps = { +colors.enabled = true +colors.weightgray = true +colors.attribute = attributes.private('color') +colors.selector = attributes.private('colormodel') +colors.default = 1 +colors.main = nil +colors.triggering = true + +storage.register("colors/values", colors.values, "colors.values") +storage.register("colors/registered", colors.registered, "colors.registered") + +local templates = { rgb = "r:%s:%s:%s", cmyk = "c:%s:%s:%s:%s", gray = "s:%s", spot = "p:%s:%s:%s:%s" } -colors.models = { +local models = { all = 1, gray = 2, rgb = 3, @@ -562,324 +87,469 @@ colors.models = { colors.model = "all" -do +local data = colors.data +local values = colors.values +local registered = colors.registered - local min = math.min - local max = math.max +local numbers = attributes.numbers +local list = attributes.list - 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 min = math.min +local max = math.max - 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 nodeinjections = backends.nodeinjections +local codeinjections = backends.codeinjections +local registrations = backends.registrations - local function graydata(s) - return backends.pdf.literal(format("%s g %s G",s,s)) - end +local function rgbtocmyk(r,g,b) -- we could reduce + return 1-r, 1-g, 1-b, 0 +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 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 rgbtocmyk(r,g,b) -- we could reduce - return 1-r, 1-g, 1-b, 0 +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 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 cmyktogray(c,m,y,k) + return rgbtogray(cmyktorgb(c,m,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 +colors.rgbtocmyk = rgbtocmyk +colors.rgbtogray = rgbtogray +colors.cmyktorgb = cmyktorgb +colors.cmyktogray = cmyktogray - local function cmyktogray(c,m,y,k) - return rgbtogray(cmyktorgb(c,m,y,k)) - end +-- 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 - colors.rgbtocmyk = rgbtocmyk - colors.rgbtogray = rgbtogray - colors.cmyktorgb = cmyktorgb - colors.cmyktogray = cmyktogray +function colors.gray(s) + return { 2, s, s, s, s, 0, 0, 0, 1-s } +end - -- 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.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.gray(s) - return { 2, s, s, s, s, 0, 0, 0, 1-s } - 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.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.spot(parent,f,d,p) +--~ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p } +--~ 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 } +function colors.spot(parent,f,d,p) + if type(p) == "number" then + local n = list[numbers.color][parent] -- hard coded ref to color number + if n then + local v = 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.spot(parent,f,d,p) ---~ if type(p) == "string" and p:find(",") then ---~ -- use converted replacement (combination color) ---~ else ---~ -- todo: map gray, rgb, cmyk onto fraction*parent ---~ end - return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p } - end +local function graycolor(...) graycolor = nodeinjections.graycolor return graycolor(...) end +local function rgbcolor (...) rgbcolor = nodeinjections.rgbcolor return rgbcolor (...) end +local function cmykcolor(...) cmykcolor = nodeinjections.cmykcolor return cmykcolor(...) end +local function spotcolor(...) spotcolor = nodeinjections.spotcolor return spotcolor(...) 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",format("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 +local function extender(colors,key) + if key == "none" then + local d = graycolor(0) + colors.none = d return d end +end - function colors.filter(n) - return concat(colors.data[n],":",5) +local function reviver(data,n) + local v = values[n] + local d + if not v then + local gray = graycolor(0) + d = { gray, gray, gray, gray } + logs.report("attributes","unable to revive color %s",n or "?") + else + local kind, gray, rgb, cmyk = v[1], graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(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 = spotcolor(v[10],v[11],v[12],v[13]) + d = { spot, gray, rgb, cmyk } + end end + data[n] = d + return d +end - colors.none = graydata(0) +setmetatable(colors, { __index = extender }) +setmetatable(colors.data, { __index = reviver }) +function colors.filter(n) + return concat(data[n],":",5) end -function colors.setmodel(attribute,name) +function colors.setmodel(name,weightgray) colors.model = name - colors.selector = attributes.numbers[attribute] - colors.default = colors.models[name] or 1 + colors.default = models[name] or 1 + colors.weightgray = weightgray ~= false 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] +function colors.register(name, colorspace, ...) -- passing 9 vars is faster (but not called that often) + local stamp = format(templates[colorspace],...) + local color = registered[stamp] if not color then - color = #colors.values+1 - colors.values[color] = colors[colorspace](...) - colors.registered[stamp] = color - colors.reviver(color) + color = #values+1 + values[color] = colors[colorspace](...) + registered[stamp] = color + -- colors.reviver(color) end if name then - attributes.list[attributes.numbers[attribute]][name] = color -- not grouped, so only global colors + list[colors.attribute][name] = color -- not grouped, so only global colors end - return colors.registered[stamp] + return registered[stamp] end function colors.value(id) - return colors.values[id] + return values[id] end -shipouts.plugins.color = { +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, + 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 +transparencies.attribute = attributes.private('transparency') + +storage.register("transparencies/registered", transparencies.registered, "transparencies.registered") +storage.register("transparencies/values", transparencies.values, "transparencies.values") -input.storage.register(false, "transparencies/registered", transparencies.registered, "transparencies.registered") -input.storage.register(false, "transparencies/values", transparencies.values, "transparencies.values") +local registered = transparencies.registered -- we could use a 2 dimensional table instead +local data = transparencies.data +local values = transparencies.values +local template = "%s:%s" + +local function inject_transparency (...) + inject_transparency = nodeinjections.transparency + return inject_transparency(...) +end -function transparencies.reference(n) - return backends.pdf.literal(format("/Tr%s gs",n)) +local function register_transparency(...) + register_transparency = registrations.transparency + return register_transparency(...) end -function transparencies.register(name,a,t) - local stamp = format(transparencies.template,a,t) - local n = transparencies.registered[stamp] +function transparencies.register(name,a,t,force) -- name is irrelevant here (can even be nil) + -- Force needed here for metapost converter. We could always force + -- but then we'd end up with transparencies resources even if we + -- would not use transparencies (but define them only). This is + -- somewhat messy. + local stamp = format(template,a,t) + local n = 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",format("unable to revive transparency %s",n or "?")) - else - d = transparencies.reference(n) - states.collect(format("\\presetPDFtransparencybynumber{%s}{%s}{%s}",n,v[1],v[2])) + n = #values + 1 + values[n] = { a, t } + registered[stamp] = n + if force then + register_transparency(n,a,t) end - transparencies.data[n] = d + elseif force and not data[n] then + register_transparency(n,a,t) end - return d + return registered[stamp] end --- check if there is an identity +local function extender(transparencies,key) + if key == "none" then + local d = inject_transparency(0) + transparencies.none = d + return d + end +end ---~ transparencies.none = transparencies.reference(transparencies.register(nil,1,1)) +local function reviver(data,n) + local v = values[n] + local d + if not v then + d = inject_transparency(0) + else + d = inject_transparency(n) + register_transparency(n,v[1],v[2]) + end + data[n] = d + return d +end + +setmetatable(transparencies, { __index = extender }) +setmetatable(transparencies.data, { __index = reviver }) -- register if used -transparencies.none = transparencies.reference(0) -- for the moment the pdf backend does this +-- check if there is an identity function transparencies.value(id) - return transparencies.values[id] + return values[id] end -shipouts.plugins.transparency = { +shipouts.handle_transparency = nodes.install_attribute_handler { + name = "transparency", namespace = transparencies, initializer = states.initialize, - finalizer = states.finalize , - processor = states.process , + 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[1] +overprints = overprints or { } +overprints.data = overprints.data or { } +overprints.enabled = false +overprints.attribute = attributes.private('overprint') overprints.registered = { overprint = 1, knockout = 2, } +local data, registered = overprints.data, overprints.registered + +local function extender(overprints,key) + if key == "none" then + local d = data[2] + overprints.none = d + return d + end +end + +local function reviver(data,n) + if n == 1 then + local d = nodeinjections.overprint() -- called once + data[1] = d + return d + elseif n == 2 then + local d = nodeinjections.knockout() -- called once + data[2] = d + return d + end +end + +setmetatable(overprints, { __index = extender }) +setmetatable(overprints.data, { __index = reviver }) + function overprints.register(stamp) --- states.collect(tex.sprint(tex.ctxcatcodes,"\\initializePDFoverprint")) -- to be testd - return overprints.registered[stamp] or overprints.registered.overprint + return registered[stamp] or registered.overprint end -shipouts.plugins.overprint = { +shipouts.handle_overprint = nodes.install_attribute_handler { + name = "overprint", namespace = overprints, initializer = states.initialize, - finalizer = states.finalize , - processor = states.process , + finalizer = states.finalize, + processor = states.process, } --- negative / positive -negatives = netatives 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 = negatives or { } +negatives.data = negatives.data or { } +negatives.enabled = false +negatives.attribute = attributes.private("negative") negatives.registered = { positive = 1, negative = 2, } +local data, registered = negatives.data, negatives.registered + +local function extender(negatives,key) + if key == "none" then + local d = data[1] + negatives.none = d + return d + end +end + +local function reviver(data,n) + if n == 1 then + local d = nodeinjections.positive() -- called once + data[1] = d + return d + elseif n == 2 then + local d = nodeinjections.negative() -- called once + data[2] = d + return d + end +end + +setmetatable(negatives, { __index = extender }) +setmetatable(negatives.data, { __index = reviver }) + function negatives.register(stamp) --- states.collect(tex.sprint(tex.ctxcatcodes,"\\initializePDFnegative")) -- to be testd - return negatives.registered[stamp] or negatives.registered.positive + return registered[stamp] or registered.positive end -shipouts.plugins.negative = { +shipouts.handle_negative = nodes.install_attribute_handler { + name = "negative", namespace = negatives, initializer = states.initialize, finalizer = states.finalize, processor = states.process, } --- effects +-- effects -- can be optimized (todo: metatables) effects = effects or { } effects.data = effects.data or { } +effects.values = effects.values or { } effects.registered = effects.registered or { } effects.enabled = false effects.stamp = "%s:%s:%s" +effects.attribute = attributes.private("effect") -input.storage.register(false, "effects/registered", effects.registered, "effects.registered") -input.storage.register(false, "effects/data", effects.data, "effects.data") +storage.register("effects/registered", effects.registered, "effects.registered") +storage.register("effects/values", effects.values, "effects.values") -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 +local data, registered, values = effects.data, effects.registered, effects.values + +-- valid effects: normal inner outer both hidden (stretch,rulethickness,effect) + +local function effect(...) effect = nodeinjections.effect return effect(...) end + +local function extender(effects,key) + if key == "none" then + local d = effect(0,0,0) + effects.none = d + return d 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 +local function reviver(data,n) + local e = values[n] -- we could nil values[n] now but hardly needed + local d = effect(v[1],v[2],v[3]) + data[n] = d + return d end -effects.none = effects.reference(0,0,0) -- faster: backends.pdf.literal("0 Tc 0 w 0 Tr") +setmetatable(effects, { __index = extender }) +setmetatable(effects.data, { __index = reviver }) + +function effects.register(effect,stretch,rulethickness) + local stamp = format(effects.stamp,effect,stretch,rulethickness) + local n = registered[stamp] + if not n then + n = #values + 1 + values[n] = { effect, stretch, rulethickness } + registered[stamp] = n + end + return n +end -shipouts.plugins.effect = { +shipouts.handle_effect = nodes.install_attribute_handler { + name = "effect", namespace = effects, initializer = states.initialize, finalizer = states.finalize, processor = states.process, } --- layers +-- layers (ugly code, due to no grouping and such); currently we use exclusive layers +-- but when we need it stacked layers might show up too; the next function based +-- approach can be replaced by static (metatable driven) resolvers + +viewerlayers = viewerlayers or { } +viewerlayers.data = viewerlayers.data or { } +viewerlayers.registered = viewerlayers.registered or { } +viewerlayers.values = viewerlayers.values or { } +viewerlayers.listwise = viewerlayers.listwise or { } +viewerlayers.enabled = false +viewerlayers.attribute = attributes.private("viewerlayer") + +storage.register("viewerlayers/registered", viewerlayers.registered, "viewerlayers.registered") +storage.register("viewerlayers/values", viewerlayers.values, "viewerlayers.values") + +local data = viewerlayers.data +local values = viewerlayers.values +local listwise = viewerlayers.listwise +local registered = viewerlayers.registered +local template = "%s" + +-- stacked + +local function extender(viewerlayers,key) + if key == "none" then + local d = nodeinjections.stoplayer() + viewerlayers.none = d + return d + end +end + +local function reviver(data,n) + local d = nodeinjections.startlayer(values[n]) + data[n] = d + return d +end ---~ /OC /somename BDC ---~ EMC +setmetatable(viewerlayers, { __index = extender }) +setmetatable(viewerlayers.data, { __index = reviver }) + +local function initializer(...) + return states.initialize(...) +end + +viewerlayers.register = function(name,lw) -- if not inimode redefine data[n] in first call + local stamp = format(template,name) + local n = registered[stamp] + if not n then + n = #values + 1 + values[n] = name + registered[stamp] = n + listwise[n] = lw or false + end + return registered[stamp] -- == n +end + +shipouts.handle_viewerlayer = nodes.install_attribute_handler { + name = "viewerlayer", + namespace = viewerlayers, + initializer = initializer, + finalizer = states.finalize, + processor = states.stacked, +} |