if not modules then modules = { } end modules ['page-ini'] = { version = 1.001, comment = "companion to page-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- Some day I need to make this more efficient. local tonumber, rawget, rawset, type, next = tonumber, rawget, rawset, type, next local match = string.match local sort, tohash, insert, remove, sortedkeys = table.sort, table.tohash, table.insert, table.remove, table.sortedkeys local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash local texgetcount = tex.getcount local tonut = nodes.tonut local nextlist = nodes.nuts.traversers.list local texlists = tex.lists local context = context local ctx_doif = commands.doif local ctx_doifelse = commands.doifelse local implement = interfaces.implement local data = table.setmetatableindex("table") local last = 0 local pages = structures.pages local autolist = { } local report = logs.reporter("pages","mark") local active = false local trace = false trackers.register("pages.mark",function(v) trace = v end) local c_realpageno = tex.iscount("realpageno") function pages.mark(name,list,settings) active = true -- local realpage = texgetcount(c_realpageno) if type(settings) == "string" then settings = settings_to_hash(settings) end if not list or list == "" then if trace then report("marking current page %i as %a",realpage,name) end data[realpage][name] = settings or true return end if type(list) == "string" then list = settings_to_array(list) end if type(list) == "table" then for i=1,#list do local page = list[i] local sign = false if type(page) == "string" then local f, t = match(page,"(%d+)[:%-](%d+)") if f and t then f, t = tonumber(f), tonumber(t) if f and t and f <= t then if trace then report("marking page %i upto %i as %a",f,t,name) end for page=f,t do data[page][name] = settings or true end end page = false else local s, p = match(page,"([%+%-])(%d+)") if s then sign, page = s, p end end end if page then page = tonumber(page) if page then if sign == "+" then page = realpage + page end if sign == "-" then report("negative page numbers are not supported") else if trace then report("marking page %i as %a",page,name) end data[page][name] = settings or true end end end end else if trace then report("marking current page %i as %a",realpage,name) end data[realpage][name] = settings or true end end local tobemarked = { } function pages.markedlist(realpage) if active and realpage then local m = rawget(tobemarked,realpage) or rawget(data,realpage) return m and next(m) and sortedkeys(m) end end local function marked(name) if active then local realpage = texgetcount(c_realpageno) if last ~= 0 then for i=last,realpage-1 do -- print(last) local di = data[i] if di then tobemarked[i] = di rawset(data,i,nil) end end last = 0 -- needs checking end local pagedata = rawget(data,realpage) return pagedata and pagedata[name] and true or false else return false end end local function markedparameter(name,key) if active then local pagedata = rawget(data,texgetcount(c_realpageno)) if pagedata then pagedata = pagedata[name] if pagedata then pagedata = pagedata[key] end end return pagedata end end local function toranges(marked) local list = { } local size = #marked if size > 0 then local first = marked[1] local last = first for i=2,size do local page = marked[i] if page > last + 1 then list[#list+1] = { first, last } first = page end last = page end list[#list+1] = { first, last } -- active = true end return list end local function allmarked(list) if active and list then local collected = pages.collected if collected then if type(list) == "string" then list = settings_to_hash(list) elseif type(list) == "table" and #list > 0 then list = tohash(list) end if type(list) == "table" then local found = { } for name in next, list do for page, list in next, data do if list[name] and collected[page] then found[#found+1] = page end end end if #found > 0 then sort(found) if trace then local ranges = toranges(found) for i=1,#ranges do local range = ranges[i] local first = range[1] local last = range[2] if first == last then report("marked page : %i",first) else report("marked range: %i upto %i",first,last) end end end return found end end end end end pages.marked = marked pages.toranges = toranges pages.allmarked = allmarked -- An alternative is to use an attribute and identify the state by parsing the node -- list but that's a bit overkill for a hardly used feature like this. -- Page actions are bound to a real page. When we set one, we need to bind to the -- current page unless we just flushed. So we also need to check the current page. -- \page ... \start : sync realpage -- \page \start : sync realpage -- \page \stop : reset synced because no content yet -- \page ... \stop : keep sync local function autopageaction() if active then local nofauto = #autolist if nofauto > 0 then local realpage = texgetcount(c_realpageno) for i=1,nofauto do local entry = autolist[i] local names = entry[1] local settings = entry[2] for j=1,#names do local name = names[j] local list = data[realpage] if not list[name] then if trace then report("automatically marking page %i as %a",realpage,name) end list[name] = settings or true end end end end end end local function startmarked(name,settings) active = true -- insert(autolist, { settings_to_array(name), settings_to_hash(settings) }) autopageaction(true) end local function stopmarked() local nofauto = #autolist if nofauto > 0 then if not texlists.pagehead then local realpage = texgetcount(c_realpageno) for i=1,nofauto do local entry = autolist[i] local names = entry[1] for j=1,#names do local name = names[j] local list = data[realpage] if list[name] then if trace then report("automatically unmarking page %i as %a",realpage,name) end list[name] = nil end end end end remove(autolist) end end implement { name = "checkmarkedpages", protected = true, public = true, actions = autopageaction, } implement { name = "markpage", arguments = "3 strings", actions = pages.mark } implement { name = "doifelsemarkedpage", arguments = "argument", protected = true, public = true, actions = { marked, ctx_doifelse } } implement { name = "doifmarkedpage", arguments = "argument", protected = true, public = true, actions = { marked, ctx_doif } } implement { name = "markedpageparameter", arguments = "2 arguments", public = true, actions = function(name,key) local value = markedparameter(name,key) if value then context(value) end end } implement { name = "markedpages", arguments = "string", actions = function(name) local t = allmarked(name) if t then context("%,t",t) end end } implement { name = "startmarkpages", arguments = "2 strings", actions = startmarked, } implement { name = "stopmarkpages", protected = true, public = true, actions = stopmarked, } implement { name = "doifelsependingpagecontent", actions = function() local h = texlists.contrib_head -- local t = texlists.contrib_tail local p = false if h then for n in nextlist, tonut(h) do p = true break end end ctx_doifelse(p) end, } -- implement { -- name = "pagestate", -- public = true, -- usage = "value", -- actions = function() -- local result = 0 -- if nodes.nuts.getspeciallist("contributehead") then -- result = result | 1 -- end -- if nodes.nuts.getspeciallist("pagehead") then -- result = result | 2 -- end -- return tokens.values.integer, result -- end -- }