if not modules then modules = { } end modules ['supp-box'] = { version = 1.001, optimize = true, comment = "companion to supp-box.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local report_hyphenation = logs.reporter("languages","hyphenation") local tonumber, next, type = tonumber, next, type local lpegmatch = lpeg.match local tex = tex local context = context local nodes = nodes local implement = interfaces.implement local ctx_doifelse = commands.doifelse local nodecodes = nodes.nodecodes local disc_code = nodecodes.disc local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local glue_code = nodecodes.glue local penalty_code = nodecodes.penalty local glyph_code = nodecodes.glyph local par_code = nodecodes.par local indentlist_code = nodes.listcodes.indent local topskip_code = nodes.gluecodes.topskip local indentskip_code = nodes.gluecodes.indentskip local line_code = nodes.listcodes.line local hmode_code = tex.modelevels.horizontal local nuts = nodes.nuts local tonut = nuts.tonut local tonode = nuts.tonode local getnext = nuts.getnext local getprev = nuts.getprev local getboth = nuts.getboth local getdisc = nuts.getdisc local getid = nuts.getid local getsubtype = nuts.getsubtype local getlist = nuts.getlist local getattribute = nuts.getattribute local getbox = nuts.getbox local getdirection = nuts.getdirection local getwidth = nuts.getwidth local getheight = nuts.getheight local getdepth = nuts.getdepth local getwhd = nuts.getwhd local takebox = nuts.takebox local setlink = nuts.setlink local setboth = nuts.setboth local setnext = nuts.setnext local setprev = nuts.setprev local setbox = nuts.setbox local setlist = nuts.setlist local setdisc = nuts.setdisc local setwidth = nuts.setwidth local setheight = nuts.setheight local setdepth = nuts.setdepth local setshift = nuts.setshift local setsplit = nuts.setsplit local setattrlist = nuts.setattrlist local setwhd = nuts.setwhd local setglue = nuts.setglue local flushnode = nuts.flushnode local flushlist = nuts.flushlist local copy_node = nuts.copy local copylist = nuts.copylist local find_tail = nuts.tail local getdimensions = nuts.dimensions local hpack = nuts.hpack local vpack = nuts.vpack local traverseid = nuts.traverseid ----- traverse = nuts.traverse local free = nuts.free local findtail = nuts.tail local reverse = nuts.reverse local effectiveglue = nuts.effectiveglue local nextdisc = nuts.traversers.disc local nextdir = nuts.traversers.dir local nexthlist = nuts.traversers.hlist local countnodes = nuts.count ----- traverselist = nuts.traverselist local listtoutf = nodes.listtoutf local nodepool = nuts.pool local new_penalty = nodepool.penalty local new_hlist = nodepool.hlist local new_glue = nodepool.glue local setlistcolor = nodes.tracers.colors.setlist local texget = tex.get local texgetbox = tex.getbox local texisdimen = tex.isdimen local texsetdimen = tex.setdimen local texgetnest = tex.getnest local values = tokens.values local dimension_value = values.dimension local cardinal_value = values.cardinal local direct_value = values.direct local conditional_value = values.conditional local d_lastnaturalboxwd = texisdimen("lastnaturalboxwd") local d_lastnaturalboxht = texisdimen("lastnaturalboxht") local d_lastnaturalboxdp = texisdimen("lastnaturalboxdp") local d_givenwidth = texisdimen("givenwidth") local d_givenheight = texisdimen("givenheight") local d_givendepth = texisdimen("givendepth") local function hyphenatedlist(head,usecolor) local current = head and tonut(head) while current do local id = getid(current) local prev, next = getboth(current) if id == disc_code then local pre, post, replace = getdisc(current) if not usecolor then -- nothing fancy done elseif pre and post then setlistcolor(pre,"darkmagenta") setlistcolor(post,"darkcyan") elseif pre then setlistcolor(pre,"darkyellow") elseif post then setlistcolor(post,"darkyellow") end if replace then flushlist(replace) end setdisc(current) if pre then setlink(prev,new_penalty(10000),pre) setlink(find_tail(pre),current) end if post then setlink(current,new_penalty(10000),post) setlink(find_tail(post),next) end elseif id == vlist_code or id == hlist_code then hyphenatedlist(getlist(current)) end current = next end end implement { name = "hyphenatedlist", arguments = { "integer", "boolean" }, actions = function(n,color) local b = texgetbox(n) if b then hyphenatedlist(b.list,color) end end } local function checkedlist(list) if type(list) == "number" then return getlist(getbox(tonut(list))) else return tonut(list) end end implement { name = "showhyphenatedinlist", arguments = "integer", actions = function(n) -- we just hyphenate (as we pass a hpack) .. a bit too much casting but ... local l = languages.hyphenators.handler(checkedlist(n)) report_hyphenation("show: %s",listtoutf(l,false,true)) end } do local function applytochars(current,doaction,noaction,nested) while current do local id = getid(current) if nested and (id == hlist_code or id == vlist_code) then context.beginhbox() applytochars(getlist(current),doaction,noaction,nested) context.endhbox() elseif id ~= glyph_code then noaction(tonode(copy_node(current))) else doaction(tonode(copy_node(current))) end current = getnext(current) end end local function applytowords(current,doaction,noaction,nested) local start while current do local id = getid(current) if id == glue_code then if start then doaction(tonode(copylist(start,current))) start = nil end noaction(tonode(copy_node(current))) elseif nested and (id == hlist_code or id == vlist_code) then context.beginhbox() applytowords(getlist(current),doaction,noaction,nested) context.egroup() elseif not start then start = current end current = getnext(current) end if start then doaction(tonode(copylist(start))) end end local methods = { char = applytochars, characters = applytochars, word = applytowords, words = applytowords, } implement { name = "applytobox", arguments = { { { "box", "integer" }, { "command" }, { "method" }, { "nested", "boolean" }, } }, actions = function(specification) local list = checkedlist(specification.box) local action = methods[specification.method or "char"] if list and action then action(list,context[specification.command or "ruledhbox"],context,specification.nested) end end } local split_char = lpeg.Ct(lpeg.C(1)^0) local split_word = lpeg.tsplitat(lpeg.patterns.space) local split_line = lpeg.tsplitat(lpeg.patterns.eol) local function processsplit(specification) local str = specification.data or "" local command = specification.command or "ruledhbox" local method = specification.method or "word" local spaced = specification.spaced if command then command = context[command] end if method == "char" or method == "character" then if spaced then spaced = context.space end local words = lpegmatch(split_char,str) for i=1,#words do local word = words[i] if word == " " then if spaced then spaced() end elseif command then command(word) else context(word) end end elseif method == "word" then if spaced then spaced = context.space end local words = lpegmatch(split_word,str) for i=1,#words do local word = words[i] if spaced and i > 1 then spaced() end if command then command(word) else context(word) end end elseif method == "line" then if spaced then spaced = context.par end local words = lpegmatch(split_line,str) for i=1,#words do local word = words[i] if spaced and i > 1 then spaced() end if command then command(word) else context(word) end end else context(str) end end implement { name = "processsplit", actions = processsplit, arguments = { { { "data" }, { "command" }, { "method" }, { "spaced", "boolean" }, } } } end do local a_vboxtohboxseparator = attributes.private("vboxtohboxseparator") implement { name = "vboxlisttohbox", arguments = { "integer", "integer", "dimen" }, actions = function(original,target,inbetween) local current = getlist(getbox(original)) local head = nil local tail = nil while current do local id = getid(current) local next = getnext(current) if id == hlist_code then local list = getlist(current) if head then if inbetween > 0 then local n = new_glue(0,0,inbetween) setlink(tail,n) tail = n end setlink(tail,list) else head = list end tail = find_tail(list) -- remove last separator if getid(tail) == hlist_code and getattribute(tail,a_vboxtohboxseparator) == 1 then local temp = tail local prev = getprev(tail) if next then local list = getlist(tail) setlink(prev,list) setlist(tail) tail = find_tail(list) else tail = prev end flushnode(temp) end -- done setnext(tail) setlist(current) end current = next end local result = new_hlist() setlist(result,head) setbox(target,result) -- setbox(target,new_hlist(head)) end } implement { name = "hboxtovbox", arguments = "integer", actions = function(n) local b = getbox(n) local factor = texget("baselineskip",false) / texget("hsize") setdepth(b,0) setheight(b,getwidth(b) * factor) end } end implement { name = "boxtostring", arguments = "integer", public = true, actions = function(n) local b = texgetbox(n) local l = b and b.list if l then -- helper is defined later local s = nodes.toutf(l) if s then context.verbatim(s) end end end } do -- we can now move this to the tex end local function getnaturaldimensions(n) local w = 0 local h = 0 local d = 0 local l = getlist(getbox(n)) if l then w, h, d = getdimensions(l) end texsetdimen(d_lastnaturalboxwd,w) texsetdimen(d_lastnaturalboxht,h) texsetdimen(d_lastnaturalboxdp,d) return w, h, d end local function setboxtonaturalwd(n) local old = takebox(n) local new = hpack(getlist(old)) setlist(old,nil) flushnode(old) setbox(n,new) end implement { name = "getnaturaldimensions", arguments = "integer", public = true, protected = true, untraced = true, actions = getnaturaldimensions } implement { name = "naturalwd", arguments = "integer", usage = "value", public = true, protected = true, actions = function(n) return dimension_value, (getnaturaldimensions(n)) end } implement { name = "getnaturalwd", arguments = "integer", usage = "value", public = true, protected = true, actions = function(n) local l = getlist(getbox(n)) return dimension_value, l and getdimensions(l) or 0 end } implement { name = "setnaturalwd", arguments = "integer", public = true, protected = true, untraced = true, actions = setboxtonaturalwd } nodes.setboxtonaturalwd = setboxtonaturalwd end do local directioncodes = tex.directioncodes local lefttoright_code = directioncodes.lefttoright local righttoleft_code = directioncodes.righttoleft local function firstdirinbox(n) local b = getbox(n) if b then local l = getlist(b) if l then for d in nextdir, l do return getdirection(d) end for h in nexthlist, l do return getdirection(h) end end end return lefttoright_code end nodes.firstdirinbox = firstdirinbox implement { name = "doifelserighttoleftinbox", arguments = "integer", actions = function(n) ctx_doifelse(firstdirinbox(n) == righttoleft_code) end } end -- new (handy for mp) .. might move to its own module do local tonut = nodes.tonut local takebox = nuts.takebox local flushlist = nuts.flushlist local copylist = nuts.copylist local getwhd = nuts.getwhd local setbox = nuts.setbox local new_hlist = nuts.pool.hlist local boxes = { } nodes.boxes = boxes local cache = table.setmetatableindex("table") local report = logs.reporter("boxes","cache") local trace = false trackers.register("nodes.boxes",function(v) trace = v end) function boxes.save(category,name,b) name = tonumber(name) or name local b = takebox(b) if trace then report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty") end if name == "+" then name = #cache[category] + 1 end cache[category][name] = b or false end function boxes.savenode(category,name,n) name = tonumber(name) or name if trace then report("category %a, name %a, %s (%s)",category,name,"save",n and "content" or "empty") end cache[category][name] = tonut(n) or false end function boxes.found(category,name) name = tonumber(name) or name return cache[category][name] and true or false end function boxes.count(category) return #cache[category] end function boxes.direct(category,name,copy) name = tonumber(name) or name local c = cache[category] local b = c[name] -- if name == "+" then -- b = remove(c, 1) -- else -- b = c[name] -- end if not b then -- do nothing, maybe trace elseif copy then b = copylist(b) else c[name] = false end if trace then report("category %a, name %a, %s (%s)",category,name,"direct",b and "content" or "empty") end if b then return tonode(b) end end function boxes.restore(category,name,box,copy) name = tonumber(name) or name local c = cache[category] local b = takebox(box) if b then flushlist(b) end local b = c[name] if not b then -- do nothing, maybe trace elseif copy then b = copylist(b) else c[name] = false end if trace then report("category %a, name %a, %s (%s)",category,name,"restore",b and "content" or "empty") end setbox(box,b or nil) end function boxes.prune(category) -- this one assumes an indexed list local c = cache[category] local t = { } local n = 0 for i=1,#c do local ci = c[i] if ci then n = n + 1 ; t[n] = ci end end cache[category] = t end local function dimensions(category,name) name = tonumber(name) or name local b = cache[category][name] if b then return getwhd(b) else return 0, 0, 0 end end boxes.dimensions = dimensions function boxes.reset(category,name) name = tonumber(name) or name local c = cache[category] if name and name ~= "" then local b = c[name] if b then flushlist(b) c[name] = false end if trace then report("category %a, name %a, reset",category,name) end else for k, b in next, c do if b then flushlist(b) end end cache[category] = { } if trace then report("category %a, reset",category) end end end function boxes.dispose(category) boxes.reset(category) cache[category] = nil -- or false (for tracing) end implement { name = "putboxincache", public = true, protected = true, arguments = { "string", "string", "integer" }, actions = boxes.save, } implement { name = "getboxfromcache", public = true, protected = true, arguments = { "string", "string", "integer" }, actions = boxes.restore, } implement { name = "directboxfromcache", public = true, protected = true, arguments = "2 strings", actions = { boxes.direct, context }, -- actions = function(category,name) local b = boxes.direct(category,name) if b then context(b) end end, } implement { name = "directcopyboxfromcache", public = true, protected = true, arguments = { "string", "string", true }, actions = { boxes.direct, context }, -- actions = function(category,name) local b = boxes.direct(category,name,true) if b then context(b) end end, } implement { name = "copyboxfromcache", public = true, protected = true, arguments = { "string", "string", "integer", true }, actions = boxes.restore, } implement { name = "doifelseboxincache", public = true, protected = true, arguments = "2 strings", actions = { boxes.found, ctx_doifelse }, } implement { name = "resetboxesincache", public = true, protected = true, arguments = "string", actions = boxes.reset, } implement { name = "pruneboxesincache", public = true, protected = true, arguments = "string", actions = boxes.prune, } implement { name = "disposeboxesincache", public = true, protected = true, arguments = "string", actions = boxes.dispose, } -- we can share this ... implement { name = "getboxcountfromcache", public = true, protected = true, usage = "value", arguments = "string", actions = function(category) return cardinal_value, #cache[category] end, } implement { name = "getboxwdfromcache", public = true, protected = true, usage = "value", arguments = "2 strings", actions = function(category,name) local w, h, d = dimensions(category,name) return dimension_value, w end, } implement { name = "getboxhtfromcache", arguments = "2 strings", public = true, protected = true, usage = "value", actions = function(category,name) local w, h, d = dimensions(category,name) return dimension_value, h end, } implement { name = "getboxdpfromcache", arguments = "2 strings", public = true, protected = true, usage = "value", actions = function(category,name) local w, h, d = dimensions(category,name) return dimension_value, d end, } implement { name = "getboxhtdpfromcache", arguments = "2 strings", public = true, protected = true, usage = "value", actions = function(category,name) local w, h, d = dimensions(category,name) return dimension_value, h + d end, } implement { name = "putnextboxincache", public = true, protected = true, arguments = { "string", "string", "box" }, actions = function(category,name,b) name = tonumber(name) or name b = tonut(b) if trace then report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty") end cache[category][name] = b or false end } end implement { name = "lastlinewidth", actions = function() local head = tex.lists.pagehead -- list dimensions returns 3 value but we take the first context(head and getdimensions(getlist(find_tail(tonut(tex.lists.pagehead)))) or 0) end } implement { name = "shiftbox", arguments = { "integer", "dimension" }, actions = function(n,d) setshift(getbox(n),d) end, } implement { name = "vpackbox", arguments = "integer", actions = function(n) setbox(n,(vpack(takebox(n)))) end } implement { name = "hpackbox", arguments = "integer", actions = function(n) setbox(n,(hpack(takebox(n)))) end } implement { name = "vpackedbox", arguments = "integer", actions = function(n) context(vpack(takebox(n))) end } implement { name = "hpackedbox", arguments = "integer", actions = function(n) context(hpack(takebox(n))) end } implement { name = "scangivendimensions", public = true, protected = true, arguments = { { { "width", "dimension" }, { "height", "dimension" }, { "depth", "dimension" }, }, }, actions = function(t) texsetdimen(d_givenwidth, t.width or 0) texsetdimen(d_givenheight,t.height or 0) texsetdimen(d_givendepth, t.depth or 0) end, } local function stripglue(list) local done = false local first = list while first do local id = getid(first) if id == glue_code or id == penalty_code then first = getnext(first) else break end end if first and first ~= list then -- we have discardables setsplit(getprev(first),first) flushlist(list) list = first done = true end if list then local tail = findtail(list) local last = tail while last do local id = getid(last) if id == glue_code or id == penalty_code then last = getprev(last) else break end end if last ~= tail then -- we have discardables flushlist(getnext(last)) setnext(last) done = true end end return list, done end local function limitate(t) -- don't pack the result ! local text = t.text if text then text = tonut(text) else return end local sentinel = t.sentinel if sentinel then sentinel = tonut(sentinel) local s = getlist(sentinel) setlist(sentinel) free(sentinel) sentinel = s else return tonode(text) end local width = getwidth(text) local list = getlist(text) local done = false if t.strip then list, done = stripglue(list) if not list then setlist(text) setwidth(text,0) return text elseif done then width = getdimensions(list) setlist(text,list) end end local left = t.left or 0 local right = t.right or 0 local total = left + right if total < width then local last = nil local first = nil local maxleft = left local maxright = right local swidth = getwidth(sentinel) if maxright > 0 then maxleft = maxleft - swidth/2 maxright = maxright - swidth/2 else maxleft = maxleft - swidth end for n in traverseid(glue_code,list) do local width = getdimensions(list,n) if width > maxleft then if not last then last = n end break else last = n end end if last and maxright > 0 then for n in traverseid(glue_code,last) do local width = getdimensions(n) if width < maxright then first = n break else first = n end end end if last then local rest = getnext(last) if rest then local tail = findtail(sentinel) if first and getid(first) == glue_code and getid(tail) == glue_code then setwidth(first,0) end if last and getid(last) == glue_code and getid(sentinel) == glue_code then setwidth(last,0) end if first and first ~= last then local prev = getprev(first) if prev then setnext(prev) end setlink(tail,first) end setlink(last,sentinel) setprev(rest) flushlist(rest) end end end setlist(text) free(text) if t.freeze then local l = hpack(list,total,"exactly") for n in traverseid(glue_code,list) do setglue(n,(effectiveglue(n,l))) end setlist(l) flushnode(l) end return tonode(list) end implement { name = "limitated", public = true, protected = true, arguments = { { { "left", "dimension" }, { "right", "dimension" }, { "text", "hbox" }, { "sentinel", "hbox" }, { "strip", "boolean" }, { "freeze", "boolean" }, } }, actions = function(t) context.dontleavehmode() context(limitate(t)) end, } -- only in lmtx: do local naturalhsize = nuts.naturalhsize implement { name = "widthuptohere", public = true, usage = "value", actions = function() local n = texgetnest() return dimension_value, n.mode == hmode_code and naturalhsize(getnext(tonut(n.head))) or 0 end, } end implement { name = "doifelseindented", public = true, protected = true, 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 n = getnext(n) if n then local id = getid(n) if id == glue_code then if getsubtype(n) == indentskip_code then b = getwidth(n) > 0 break end elseif id == hlist_code then if getsubtype(n) == indentlist_code then b = getwidth(n) > 0 end break elseif id == par_code then -- okay else break end end end end ctx_doifelse(b) end, } implement { name = "noflinesinbox", public = true, protected = false, arguments = "integer", actions = function(n) local c = 0 local b = getbox(n) if b then b = getlist(b) if b then -- for n, id in traverse(b) do -- traverse_list -- if id == hlist_code or id == vlist_code then -- c = c + 1 -- end -- end -- -- for n in traverselist(b) do -- c = c + 1 -- end -- c = countnodes(hlist_code,b) + countnodes(vlist_code,b) end end context(c) -- return cardinal_value, c end, } do local takebox = tex.takebox implement { name = "thebox", public = true, arguments = "integer", actions = function(n) context(takebox(n)) end } end -- only in lmtx implement { name = "reversevboxcontent", protected = true, public = true, arguments = "integer", actions = function(n) local b = getbox(n) local l = b and getid(b) == vlist_code and getlist(b) if l and getnext(l) then setlist(b,reverse(l)) -- no re-vpack here! end end } -- only in lmtx do local scaninteger = tokens.scanners.integer local scanbox = tokens.scanners.box local scandimen = tokens.scanners.dimen local setsubtype = nuts.setsubtype local removenode = nuts.remove local getnormalizedline = nuts.getnormalizedline -- we can optimize this local getdimensions = nuts.dimensions local getrangedimensions = nuts.rangedimensions local setprop = nuts.setprop local getprop = nuts.getprop local listcodes = nodes.listcodes local line_code = listcodes.line local equation_code = listcodes.equation local unknown_code = listcodes.unknown -- todo: make helper that formats local reporterror = logs.texerrormessage -- The first variant did a linear lookup but for large boxes and lots of -- analysis that is not nice. Okay, in practice performance is quite ok -- (milliseconds for thousands of access) but still ... the next is nicer -- and it's part of the experimental fun stuff anyway. local function countlines(box) local prop = getprop(box,"boxlines") if not prop then local line = 0 local list = getlist(box) prop = { } if list then for n, subtype in nexthlist, list do if subtype == line_code then -- or subtype == equation_code then line = line + 1 prop[line] = n end end end setprop(box,"boxlines",prop) end return prop end local function boxlinecount(what) local n = scaninteger() local box = getbox(n) if what == "value" then return cardinal_value, box and #countlines(box) or 0 end end local function findline() local n = scaninteger() local line = scaninteger() local box = getbox(n) if box then local prop = getprop(box,"boxlines") if not prop then prop = countlines(box) end local found = prop[line] if found then local props = getprop(found,"lineproperties") if not props then props = getnormalizedline(found) props.width, props.height, props.depth = getwhd(found) setprop(found,"lineproperties",props) end return props, line, found, box end end reporterror("no line %i in box %i",line,n) end local function findrange() local n = scaninteger() local first = scaninteger() local last = scaninteger() local box = getbox(n) if box then local prop = getprop(box,"boxlines") if not prop then prop = countlines(box) end if first > 0 and last <= #prop then for i = first, last do local found = prop[i] local props = getprop(found,"lineproperties") if not props then props = getnormalizedline(found) props.width, props.height, props.depth = getwhd(found) setprop(found,"lineproperties",props) end end return prop, first, last, box end end reporterror("no lines %i - %i in box %i",first,last,n) end local function getline(props,line,found,box,value) local p, n = getboth(found) local temp = new_hlist() setsubtype(temp,getsubtype(found)) setwhd(temp,getwhd(found)) if found == getlist(box) then setlink(temp,n) setlist(box,temp) else setlink(p,temp,n) end getprop(box,"boxlines")[line] = temp setboth(found) setsubtype(found, unknown_code) if value then return direct_value, found else context(tonode(found)) end end local function copyline(props,line,found,box,value) found = copy_node(found) setsubtype(found, unknown_code) if value then return direct_value, found else context(tonode(found)) end end local function setline(props,line,found,box) local p, n = getboth(found) local temp = scanbox() if temp then temp = tonut(temp) if found == getlist(box) then setlink(temp,n) setlist(box,temp) else setlink(p,temp,n) end flushnode(found) getprop(box,"boxlines")[line] = temp end end local function naturaldimensions(p,l,found) if not p.naturalwidth then p.naturalwidth, p.naturalheight, p.naturaldepth = getdimensions(getlist(found)) end return p end local function rangedimensions(p,f,l,box) local w, h, d = getrangedimensions(box,p[f],getnext(p[l]),true) return { width = w, height = h, depth = d } end local getters_one = { ["wd"] = function(p,l,found) return dimension_value, p.width end, ["ht"] = function(p,l,found) return dimension_value, p.height end, ["dp"] = function(p,l,found) return dimension_value, p.depth end, ["ls"] = function(p,l,found) return dimension_value, p.leftskip end, ["rs"] = function(p,l,found) return dimension_value, p.rightskip end, ["lh"] = function(p,l,found) return dimension_value, p.lefthangskip end, ["rh"] = function(p,l,found) return dimension_value, p.righthangskip end, ["lp"] = function(p,l,found) return dimension_value, p.parfillleftskip end, ["rp"] = function(p,l,found) return dimension_value, p.parfillrightskip end, ["il"] = function(p,l,found) return dimension_value, p.parinitleftskip end, ["ir"] = function(p,l,found) return dimension_value, p.parinitrightskip end, ["in"] = function(p,l,found) return dimension_value, p.indent end, ["nw"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalwidth end, ["nh"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalheigth end, ["nd"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturaldepth end, ["get"] = function(p,l,found,box) return getline(p,l,found,box,true) end, } local getters_two = { ["wd"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).width end, ["ht"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).height end, ["dp"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).depth end, } local setters_one = { ["wd"] = function(p,l,found) return setwidth (found,scandimen(false,false,true)) end, ["ht"] = function(p,l,found) return setheight(found,scandimen(false,false,true)) end, ["dp"] = function(p,l,found) return setdepth (found,scandimen(false,false,true)) end, ["set"] = setline, ["get"] = getline, ["copy"] = copyline, } local function boxline(name,what) local props, line, found, box = findline() if not found then -- elseif what == "value" then local getter = getters_one[name] if getter then return getter(props,line,found,box) end else local setter = setters_one[name] if setter then return setter(props,line,found,box) end end end -- local function boxrange(name,what) local prop, first, last, box = findrange() if not prop then -- elseif what == "value" then local getter = getters_two[name] if getter then return getter(prop,first,last,box) end else local setter = setters_two[name] if setter then return setter(prop,first,last,box) end end end local function define_one(name,action) implement { name = name, public = true, usage = "value", actions = function(what) return boxline(action,what) end, } end local function define_two(name,action) implement { name = name, public = true, usage = "value", actions = function(what) return boxrange(action,what) end, } end implement { name = "boxlines", public = true, usage = "value", actions = boxlinecount, } -- todoL: just inline so that s-system-macros can find them define_one("boxline", "get") define_one("setboxline", "set") define_one("copyboxline", "copy") define_one("boxlineht", "ht") define_one("boxlinedp", "dp") define_one("boxlinewd", "wd") define_one("boxlinels", "ls") define_one("boxliners", "rs") define_one("boxlinelh", "lh") define_one("boxlinerh", "rh") define_one("boxlinelp", "lp") define_one("boxlinerp", "rp") define_one("boxlineil", "il") define_one("boxlineir", "ir") define_one("boxlinein", "in") define_one("boxlinenw", "nw") define_one("boxlinenh", "nh") define_one("boxlinend", "nd") define_two("boxrangewd", "wd") define_two("boxrangeht", "ht") define_two("boxrangedp", "dp") end do local getbox = tex.getbox local setfield = nodes.setfield local getfield = nodes.getfield local flush = nodes.flushnode local copynode = nodes.copy local function get(n,field,copy) local b = getbox(n) if b then local p = getfield(b,field) if copy then p = copynode(p) else setfield(b,field) end context(p) -- todo: proper return end end local function set(n,l,field) local b = getbox(n) if b then setfield(b,field,l) else flush(l) end end implement { name = "prelistbox", public = true, usage = "value", arguments = { "integer", '"pre"' }, actions = get, } implement { name = "postlistbox", public = true, usage = "value", arguments = { "integer", '"post"' }, actions = get, } implement { name = "prelistcopy", public = true, usage = "value", arguments = { "integer", '"pre"', true }, actions = get, } implement { name = "postlistcopy", public = true, usage = "value", arguments = { "integer", '"post"', true }, actions = get, } implement { name = "setprelistbox", public = true, usage = "value", arguments = { "integer", "box", '"pre"' }, actions = set, } implement { name = "setpostlistbox", public = true, usage = "value", arguments = { "integer", "box", '"post"' }, actions = set, } end do local function setsplitlisthtdp(n,ht,dp) local box = getbox(n) if box then local head = getlist(box) if head then local tail = findtail(head) local id = getid(head) if id == glue_code and getsubtype(head) == topskip_code then head = getnext(head) id = head and getid(head) ht = ht - getwidth(head) end if id == hlist_code and getsubtype(head) == line_code and getheight(head) < ht then -- print("set height",ht) setheight(head,ht) end local id = getid(tail) if id == glue_code then tail = getprev(tail) id = tail and getid(tail) dp = dp - getwidth(tail) end if id == hlist_code and getsubtype(tail) == line_code and getdepth(tail) < dp then setdepth(dept,dp) -- print("set depth",dp) end end end end implement { name = "setsplitlisthtdp", public = true, protected = true, arguments = { "integer", "dimen", "dimen" }, actions = setsplitlisthtdp, } end do local ctx_validtext = context.core.validtext implement { name = "iftext", public = true, usage = "condition", actions = function() ctx_validtext() return conditional_value end } end